mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 03:42:27 +02:00
Merge remote-tracking branch 'leo/master' into console
This commit is contained in:
commit
02069bfcd2
41 changed files with 2857 additions and 543 deletions
56
cmake/externals/neuron/CMakeLists.txt
vendored
Normal file
56
cmake/externals/neuron/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
include(ExternalProject)
|
||||
|
||||
set(EXTERNAL_NAME neuron)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.zip")
|
||||
set(NEURON_URL_MD5 "0ab54ca04c9cc8094e0fa046c226e574")
|
||||
|
||||
ExternalProject_Add(${EXTERNAL_NAME}
|
||||
URL ${NEURON_URL}
|
||||
URL_MD5 ${NEURON_URL_MD5}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
# set include dir
|
||||
if(WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Windows/include" CACHE TYPE INTERNAL)
|
||||
elseif(APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Mac/include" CACHE TYPE INTERNAL)
|
||||
else()
|
||||
# Unsupported
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
else()
|
||||
set(ARCH_DIR "x86")
|
||||
endif()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Windows/lib/${ARCH_DIR}")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL)
|
||||
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}")
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Mac/dylib")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL)
|
||||
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}")
|
||||
|
||||
else()
|
||||
# UNSUPPORTED
|
||||
endif()
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
macro(CONSOLIDATE_STACK_COMPONENTS)
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
if (WIN32)
|
||||
# Copy all the output for this target into the common deployment location
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/full-stack-deployment
|
||||
)
|
||||
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
elseif (TARGET_NAME STREQUAL "stack-manager")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico")
|
||||
set (ICON_DESTINATION_NAME "stack-manager.ico")
|
||||
endif ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME}
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
macro(CONSOLIDATE_STACK_COMPONENTS)
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
if (WIN32)
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/${INTERFACE_ICON}")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
else ()
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/${STACK_MANAGER_ICON}")
|
||||
set (ICON_DESTINATION_NAME "stack-manager.ico")
|
||||
endif ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/package-bundle/${ICON_DESTINATION_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
else ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
endmacro()
|
|
@ -1,30 +1,30 @@
|
|||
#
|
||||
# GenerateInstallers.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Created by Leonardo Murillo on 12/16/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
|
||||
#
|
||||
|
||||
macro(GENERATE_INSTALLERS)
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/full-stack-deployment")
|
||||
find_program(MAKENSIS_COMMAND makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS])
|
||||
if (NOT MAKENSIS_COMMAND)
|
||||
message(FATAL_ERROR "The Nullsoft Scriptable Install Systems is required for generating packaged installers on Windows (http://nsis.sourceforge.net/)")
|
||||
endif ()
|
||||
add_custom_target(
|
||||
build-package ALL
|
||||
DEPENDS interface assignment-client domain-server stack-manager
|
||||
COMMAND set INSTALLER_SOURCE_DIR=${CMAKE_BINARY_DIR}/full-stack-deployment
|
||||
COMMAND set INSTALLER_NAME=${CMAKE_BINARY_DIR}/${INSTALLER_NAME}
|
||||
COMMAND set INSTALLER_SCRIPTS_DIR=${CMAKE_SOURCE_DIR}/examples
|
||||
COMMAND set INSTALLER_COMPANY=${INSTALLER_COMPANY}
|
||||
COMMAND set INSTALLER_DIRECTORY=${INSTALLER_DIRECTORY}
|
||||
COMMAND CMD /C "\"${MAKENSIS_COMMAND}\" ${CMAKE_SOURCE_DIR}/tools/nsis/release.nsi"
|
||||
)
|
||||
endif ()
|
||||
#
|
||||
# GenerateInstallers.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Created by Leonardo Murillo on 12/16/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
|
||||
#
|
||||
|
||||
macro(GENERATE_INSTALLERS)
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/package-bundle")
|
||||
find_program(MAKENSIS_COMMAND makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS])
|
||||
if (NOT MAKENSIS_COMMAND)
|
||||
message(FATAL_ERROR "The Nullsoft Scriptable Install Systems is required for generating packaged installers on Windows (http://nsis.sourceforge.net/)")
|
||||
endif ()
|
||||
add_custom_target(
|
||||
build-package ALL
|
||||
DEPENDS interface assignment-client domain-server stack-manager
|
||||
COMMAND set INSTALLER_SOURCE_DIR=${CMAKE_BINARY_DIR}/package-bundle
|
||||
COMMAND set INSTALLER_NAME=${CMAKE_BINARY_DIR}/${INSTALLER_NAME}
|
||||
COMMAND set INSTALLER_SCRIPTS_DIR=${CMAKE_SOURCE_DIR}/examples
|
||||
COMMAND set INSTALLER_COMPANY=${INSTALLER_COMPANY}
|
||||
COMMAND set INSTALLER_DIRECTORY=${INSTALLER_DIRECTORY}
|
||||
COMMAND CMD /C "\"${MAKENSIS_COMMAND}\" ${CMAKE_SOURCE_DIR}/tools/nsis/release.nsi"
|
||||
)
|
||||
endif ()
|
||||
endmacro()
|
|
@ -19,17 +19,23 @@ macro(INCLUDE_APPLICATION_VERSION)
|
|||
set(INSTALLER_COMPANY "High Fidelity")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}")
|
||||
set(INSTALLER_NAME "interface-win64-${BUILD_SEQ}.exe")
|
||||
set(INTERFACE_ICON "interface.ico")
|
||||
set(STACK_MANAGER_ICON "icon.ico")
|
||||
elseif (DEFINED ENV{ghprbPullId})
|
||||
set(DEPLOY_PACKAGE 1)
|
||||
set(BUILD_SEQ "PR-$ENV{ghprbPullId}")
|
||||
set(INSTALLER_COMPANY "High Fidelity - PR")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}\\${BUILD_SEQ}")
|
||||
set(INSTALLER_NAME "pr-interface-win64-${BUILD_SEQ}.exe")
|
||||
set(INTERFACE_ICON "interface-beta.ico")
|
||||
set(STACK_MANAGER_ICON "icon-beta.ico")
|
||||
else ()
|
||||
set(BUILD_SEQ "dev")
|
||||
set(INSTALLER_COMPANY "High Fidelity - Dev")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}")
|
||||
set(INSTALLER_NAME "dev-interface-win64.exe")
|
||||
set(INTERFACE_ICON "interface-beta.ico")
|
||||
set(STACK_MANAGER_ICON "icon-beta.ico")
|
||||
endif ()
|
||||
configure_file("${MACRO_DIR}/ApplicationVersion.h.in" "${PROJECT_BINARY_DIR}/includes/ApplicationVersion.h")
|
||||
include_directories("${PROJECT_BINARY_DIR}/includes")
|
||||
|
|
17
cmake/macros/TargetNeuron.cmake
Normal file
17
cmake/macros/TargetNeuron.cmake
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Anthony J. Thibault on 2015/12/21
|
||||
#
|
||||
# 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_NEURON)
|
||||
# Neuron data reader is only available on these platforms
|
||||
if (WIN32 OR APPLE)
|
||||
add_dependency_external_projects(neuron)
|
||||
find_package(Neuron REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${NEURON_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${NEURON_LIBRARIES})
|
||||
add_definitions(-DHAVE_NEURON)
|
||||
endif(WIN32 OR APPLE)
|
||||
endmacro()
|
28
cmake/modules/FindNeuron.cmake
Normal file
28
cmake/modules/FindNeuron.cmake
Normal file
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# FindNeuron.cmake
|
||||
#
|
||||
# Try to find the Perception Neuron SDK
|
||||
#
|
||||
# You must provide a NEURON_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# NEURON_FOUND - system found Neuron SDK
|
||||
# NEURON_INCLUDE_DIRS - the Neuron SDK include directory
|
||||
# NEURON_LIBRARIES - Link this to use Neuron
|
||||
#
|
||||
# Created on 12/21/2015 by Anthony J. Thibault
|
||||
# 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
|
||||
#
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(NEURON)
|
||||
|
||||
set(NEURON_REQUIREMENTS NEURON_INCLUDE_DIRS NEURON_LIBRARIES)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Neuron DEFAULT_MSG NEURON_INCLUDE_DIRS NEURON_LIBRARIES)
|
||||
mark_as_advanced(NEURON_LIBRARIES NEURON_INCLUDE_DIRS NEURON_SEARCH_DIRS)
|
||||
|
|
@ -258,7 +258,7 @@ if (WIN32)
|
|||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${OPENSSL_DLL_PATH}/ssleay32.dll ${CMAKE_BINARY_DIR}/full-stack-deployment/
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${OPENSSL_DLL_PATH}/ssleay32.dll ${CMAKE_BINARY_DIR}/package-bundle/
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
|
@ -23,8 +23,9 @@ var WANT_DEBUG = false;
|
|||
// these tune time-averaging and "on" value for analog trigger
|
||||
//
|
||||
|
||||
var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value
|
||||
var TRIGGER_ON_VALUE = 0.4;
|
||||
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
|
||||
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
|
||||
var TRIGGER_OFF_VALUE = 0.15;
|
||||
|
||||
var BUMPER_ON_VALUE = 0.5;
|
||||
|
@ -96,7 +97,7 @@ var MSEC_PER_SEC = 1000.0;
|
|||
var LIFETIME = 10;
|
||||
var ACTION_TTL = 15; // seconds
|
||||
var ACTION_TTL_REFRESH = 5;
|
||||
var PICKS_PER_SECOND_PER_HAND = 5;
|
||||
var PICKS_PER_SECOND_PER_HAND = 60;
|
||||
var MSECS_PER_SEC = 1000.0;
|
||||
var GRABBABLE_PROPERTIES = [
|
||||
"position",
|
||||
|
@ -123,8 +124,8 @@ var blacklist = [];
|
|||
|
||||
//we've created various ways of visualizing looking for and moving distant objects
|
||||
var USE_ENTITY_LINES_FOR_SEARCHING = false;
|
||||
var USE_OVERLAY_LINES_FOR_SEARCHING = false;
|
||||
var USE_PARTICLE_BEAM_FOR_SEARCHING = true;
|
||||
var USE_OVERLAY_LINES_FOR_SEARCHING = true;
|
||||
var USE_PARTICLE_BEAM_FOR_SEARCHING = false;
|
||||
|
||||
var USE_ENTITY_LINES_FOR_MOVING = false;
|
||||
var USE_OVERLAY_LINES_FOR_MOVING = false;
|
||||
|
@ -290,6 +291,11 @@ function MyController(hand) {
|
|||
this.spotlight = null;
|
||||
this.pointlight = null;
|
||||
this.overlayLine = null;
|
||||
this.searchSphere = null;
|
||||
|
||||
// how far from camera to search intersection?
|
||||
this.intersectionDistance = 0.0;
|
||||
this.searchSphereDistance = 0.0;
|
||||
|
||||
this.ignoreIK = false;
|
||||
this.offsetPosition = Vec3.ZERO;
|
||||
|
@ -409,6 +415,23 @@ function MyController(hand) {
|
|||
}
|
||||
};
|
||||
|
||||
var SEARCH_SPHERE_ALPHA = 0.5;
|
||||
this.searchSphereOn = function(location, size, color) {
|
||||
if (this.searchSphere === null) {
|
||||
var sphereProperties = {
|
||||
position: location,
|
||||
size: size,
|
||||
color: color,
|
||||
alpha: SEARCH_SPHERE_ALPHA,
|
||||
solid: true,
|
||||
visible: true
|
||||
}
|
||||
this.searchSphere = Overlays.addOverlay("sphere", sphereProperties);
|
||||
} else {
|
||||
Overlays.editOverlay(this.searchSphere, { position: location, size: size, color: color, visible: true });
|
||||
}
|
||||
}
|
||||
|
||||
this.overlayLineOn = function(closePoint, farPoint, color) {
|
||||
if (this.overlayLine === null) {
|
||||
var lineProperties = {
|
||||
|
@ -654,6 +677,17 @@ function MyController(hand) {
|
|||
this.overlayLine = null;
|
||||
};
|
||||
|
||||
this.searchSphereOff = function() {
|
||||
if (this.searchSphere !== null) {
|
||||
//Overlays.editOverlay(this.searchSphere, { visible: false });
|
||||
Overlays.deleteOverlay(this.searchSphere);
|
||||
this.searchSphere = null;
|
||||
this.searchSphereDistance = 0.0;
|
||||
this.intersectionDistance = 0.0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.particleBeamOff = function() {
|
||||
if (this.particleBeam !== null) {
|
||||
Entities.editEntity(this.particleBeam, {
|
||||
|
@ -687,6 +721,7 @@ function MyController(hand) {
|
|||
if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) {
|
||||
this.particleBeamOff();
|
||||
}
|
||||
this.searchSphereOff();
|
||||
};
|
||||
|
||||
this.triggerPress = function(value) {
|
||||
|
@ -704,6 +739,10 @@ function MyController(hand) {
|
|||
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
|
||||
};
|
||||
|
||||
this.triggerSmoothedGrab = function() {
|
||||
return this.triggerValue > TRIGGER_GRAB_VALUE;
|
||||
};
|
||||
|
||||
this.triggerSmoothedSqueezed = function() {
|
||||
return this.triggerValue > TRIGGER_ON_VALUE;
|
||||
};
|
||||
|
@ -712,11 +751,6 @@ function MyController(hand) {
|
|||
return this.triggerValue < TRIGGER_OFF_VALUE;
|
||||
};
|
||||
|
||||
this.triggerSqueezed = function() {
|
||||
var triggerValue = this.rawTriggerValue;
|
||||
return triggerValue > TRIGGER_ON_VALUE;
|
||||
};
|
||||
|
||||
this.bumperSqueezed = function() {
|
||||
return _this.rawBumperValue > BUMPER_ON_VALUE;
|
||||
};
|
||||
|
@ -726,15 +760,15 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.off = function() {
|
||||
if (this.triggerSmoothedSqueezed()) {
|
||||
if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) {
|
||||
this.lastPickTime = 0;
|
||||
this.setState(STATE_SEARCHING);
|
||||
return;
|
||||
}
|
||||
if (this.bumperSqueezed()) {
|
||||
this.lastPickTime = 0;
|
||||
this.setState(STATE_EQUIP_SEARCHING);
|
||||
return;
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
if (this.triggerSmoothedSqueezed()) {
|
||||
this.setState(STATE_SEARCHING);
|
||||
} else {
|
||||
this.setState(STATE_EQUIP_SEARCHING);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -746,15 +780,20 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
|
||||
// the trigger is being pressed, do a ray test
|
||||
// the trigger is being pressed, so do a ray test to see what we are hitting
|
||||
var handPosition = this.getHandPosition();
|
||||
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation));
|
||||
|
||||
var distantPickRay = {
|
||||
origin: handPosition,
|
||||
direction: Quat.getUp(this.getHandRotation()),
|
||||
origin: Camera.position,
|
||||
direction: Quat.getFront(Quat.multiply(Camera.orientation, handDeltaRotation)),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
// don't pick 60x per second.
|
||||
// Pick at some maximum rate, not always
|
||||
var pickRays = [];
|
||||
var now = Date.now();
|
||||
if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
|
||||
|
@ -793,7 +832,7 @@ function MyController(hand) {
|
|||
if (intersection.intersects) {
|
||||
|
||||
// the ray is intersecting something we can move.
|
||||
var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||
this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA);
|
||||
var defaultDisableNearGrabData = {
|
||||
|
@ -809,11 +848,11 @@ function MyController(hand) {
|
|||
if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) {
|
||||
continue;
|
||||
}
|
||||
if (intersectionDistance > pickRay.length) {
|
||||
if (this.intersectionDistance > pickRay.length) {
|
||||
// too far away for this ray.
|
||||
continue;
|
||||
}
|
||||
if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) {
|
||||
if (this.intersectionDistance <= NEAR_PICK_MAX_DISTANCE) {
|
||||
// the hand is very close to the intersected object. go into close-grabbing mode.
|
||||
if (grabbableData.wantsTrigger) {
|
||||
this.grabbedEntity = intersection.entityID;
|
||||
|
@ -851,11 +890,11 @@ function MyController(hand) {
|
|||
// this.setState(STATE_EQUIP_SPRING);
|
||||
this.setState(STATE_EQUIP);
|
||||
return;
|
||||
} else if (this.state == STATE_SEARCHING) {
|
||||
} else if ((this.state == STATE_SEARCHING) && this.triggerSmoothedGrab()) {
|
||||
this.setState(STATE_DISTANCE_HOLDING);
|
||||
return;
|
||||
}
|
||||
} else if (grabbableData.wantsTrigger) {
|
||||
} else if (grabbableData.wantsTrigger && this.triggerSmoothedGrab()) {
|
||||
this.grabbedEntity = intersection.entityID;
|
||||
this.setState(STATE_FAR_TRIGGER);
|
||||
return;
|
||||
|
@ -864,6 +903,7 @@ function MyController(hand) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// forward ray test failed, try sphere test.
|
||||
if (WANT_DEBUG) {
|
||||
Entities.addEntity({
|
||||
|
@ -970,14 +1010,23 @@ function MyController(hand) {
|
|||
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
if (USE_OVERLAY_LINES_FOR_SEARCHING === true) {
|
||||
this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) {
|
||||
this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
if (this.intersectionDistance > 0) {
|
||||
var SPHERE_INTERSECTION_SIZE = 0.011;
|
||||
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
|
||||
var SEARCH_SPHERE_CHASE_DROP = 0.2;
|
||||
this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE + this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE);
|
||||
var searchSphereLocation = Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
|
||||
searchSphereLocation.y -= ((this.intersectionDistance - this.searchSphereDistance) / this.intersectionDistance) * SEARCH_SPHERE_CHASE_DROP;
|
||||
this.searchSphereOn(searchSphereLocation, SPHERE_INTERSECTION_SIZE * this.intersectionDistance, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
if (USE_OVERLAY_LINES_FOR_SEARCHING === true) {
|
||||
var OVERLAY_BEAM_SETBACK = 0.9;
|
||||
var startBeam = Vec3.sum(handPosition, Vec3.multiply(Vec3.subtract(searchSphereLocation, handPosition), OVERLAY_BEAM_SETBACK));
|
||||
this.overlayLineOn(startBeam, searchSphereLocation, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.distanceHolding = function() {
|
||||
|
|
239
examples/controllers/neuron/neuronAvatar.js
Normal file
239
examples/controllers/neuron/neuronAvatar.js
Normal file
|
@ -0,0 +1,239 @@
|
|||
var JOINT_PARENT_MAP = {
|
||||
Hips: "",
|
||||
RightUpLeg: "Hips",
|
||||
RightLeg: "RightUpLeg",
|
||||
RightFoot: "RightLeg",
|
||||
LeftUpLeg: "Hips",
|
||||
LeftLeg: "LeftUpLeg",
|
||||
LeftFoot: "LeftLeg",
|
||||
Spine: "Hips",
|
||||
Spine1: "Spine",
|
||||
Spine2: "Spine1",
|
||||
Spine3: "Spine2",
|
||||
Neck: "Spine3",
|
||||
Head: "Neck",
|
||||
RightShoulder: "Spine3",
|
||||
RightArm: "RightShoulder",
|
||||
RightForeArm: "RightArm",
|
||||
RightHand: "RightForeArm",
|
||||
RightHandThumb1: "RightHand",
|
||||
RightHandThumb2: "RightHandThumb1",
|
||||
RightHandThumb3: "RightHandThumb2",
|
||||
RightHandThumb4: "RightHandThumb3",
|
||||
RightHandIndex1: "RightHand",
|
||||
RightHandIndex2: "RightHandIndex1",
|
||||
RightHandIndex3: "RightHandIndex2",
|
||||
RightHandIndex4: "RightHandIndex3",
|
||||
RightHandMiddle1: "RightHand",
|
||||
RightHandMiddle2: "RightHandMiddle1",
|
||||
RightHandMiddle3: "RightHandMiddle2",
|
||||
RightHandMiddle4: "RightHandMiddle3",
|
||||
RightHandRing1: "RightHand",
|
||||
RightHandRing2: "RightHandRing1",
|
||||
RightHandRing3: "RightHandRing2",
|
||||
RightHandRing4: "RightHandRing3",
|
||||
RightHandPinky1: "RightHand",
|
||||
RightHandPinky2: "RightHandPinky1",
|
||||
RightHandPinky3: "RightHandPinky2",
|
||||
RightHandPinky4: "RightHandPinky3",
|
||||
LeftShoulder: "Spine3",
|
||||
LeftArm: "LeftShoulder",
|
||||
LeftForeArm: "LeftArm",
|
||||
LeftHand: "LeftForeArm",
|
||||
LeftHandThumb1: "LeftHand",
|
||||
LeftHandThumb2: "LeftHandThumb1",
|
||||
LeftHandThumb3: "LeftHandThumb2",
|
||||
LeftHandThumb4: "LeftHandThumb3",
|
||||
LeftHandIndex1: "LeftHand",
|
||||
LeftHandIndex2: "LeftHandIndex1",
|
||||
LeftHandIndex3: "LeftHandIndex2",
|
||||
LeftHandIndex4: "LeftHandIndex3",
|
||||
LeftHandMiddle1: "LeftHand",
|
||||
LeftHandMiddle2: "LeftHandMiddle1",
|
||||
LeftHandMiddle3: "LeftHandMiddle2",
|
||||
LeftHandMiddle4: "LeftHandMiddle3",
|
||||
LeftHandRing1: "LeftHand",
|
||||
LeftHandRing2: "LeftHandRing1",
|
||||
LeftHandRing3: "LeftHandRing2",
|
||||
LeftHandRing4: "LeftHandRing3",
|
||||
LeftHandPinky1: "LeftHand",
|
||||
LeftHandPinky2: "LeftHandPinky1",
|
||||
LeftHandPinky3: "LeftHandPinky2",
|
||||
LeftHandPinky: "LeftHandPinky3",
|
||||
};
|
||||
|
||||
var USE_TRANSLATIONS = false;
|
||||
|
||||
// ctor
|
||||
function Xform(rot, pos) {
|
||||
this.rot = rot;
|
||||
this.pos = pos;
|
||||
};
|
||||
Xform.mul = function (lhs, rhs) {
|
||||
var rot = Quat.multiply(lhs.rot, rhs.rot);
|
||||
var pos = Vec3.sum(lhs.pos, Vec3.multiplyQbyV(lhs.rot, rhs.pos));
|
||||
return new Xform(rot, pos);
|
||||
};
|
||||
Xform.prototype.inv = function () {
|
||||
var invRot = Quat.inverse(this.rot);
|
||||
var invPos = Vec3.multiply(-1, this.pos);
|
||||
return new Xform(invRot, Vec3.multiplyQbyV(invRot, invPos));
|
||||
};
|
||||
Xform.prototype.toString = function () {
|
||||
var rot = this.rot;
|
||||
var pos = this.pos;
|
||||
return "Xform rot = (" + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "), pos = (" + pos.x + ", " + pos.y + ", " + pos.z + ")";
|
||||
};
|
||||
|
||||
function dumpHardwareMapping() {
|
||||
Object.keys(Controller.Hardware).forEach(function (deviceName) {
|
||||
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
|
||||
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ctor
|
||||
function NeuronAvatar() {
|
||||
var self = this;
|
||||
Script.scriptEnding.connect(function () {
|
||||
self.shutdown();
|
||||
});
|
||||
Controller.hardwareChanged.connect(function () {
|
||||
self.hardwareChanged();
|
||||
});
|
||||
|
||||
if (Controller.Hardware.Neuron) {
|
||||
this.activate();
|
||||
} else {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
NeuronAvatar.prototype.shutdown = function () {
|
||||
this.deactivate();
|
||||
};
|
||||
|
||||
NeuronAvatar.prototype.hardwareChanged = function () {
|
||||
if (Controller.Hardware.Neuron) {
|
||||
this.activate();
|
||||
} else {
|
||||
this.deactivate();
|
||||
}
|
||||
};
|
||||
|
||||
NeuronAvatar.prototype.activate = function () {
|
||||
if (!this._active) {
|
||||
Script.update.connect(updateCallback);
|
||||
}
|
||||
this._active = true;
|
||||
|
||||
// build absDefaultPoseMap
|
||||
this._defaultAbsRotMap = {};
|
||||
this._defaultAbsPosMap = {};
|
||||
this._defaultAbsRotMap[""] = {x: 0, y: 0, z: 0, w: 1};
|
||||
this._defaultAbsPosMap[""] = {x: 0, y: 0, z: 0};
|
||||
var keys = Object.keys(JOINT_PARENT_MAP);
|
||||
var i, l = keys.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
var jointName = keys[i];
|
||||
var j = MyAvatar.getJointIndex(jointName);
|
||||
var parentJointName = JOINT_PARENT_MAP[jointName];
|
||||
this._defaultAbsRotMap[jointName] = Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointRotation(j));
|
||||
this._defaultAbsPosMap[jointName] = Vec3.sum(this._defaultAbsPosMap[parentJointName],
|
||||
Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointTranslation(j)));
|
||||
}
|
||||
};
|
||||
|
||||
NeuronAvatar.prototype.deactivate = function () {
|
||||
if (this._active) {
|
||||
var self = this;
|
||||
Script.update.disconnect(updateCallback);
|
||||
}
|
||||
this._active = false;
|
||||
MyAvatar.clearJointsData();
|
||||
};
|
||||
|
||||
NeuronAvatar.prototype.update = function (deltaTime) {
|
||||
|
||||
var hmdActive = HMD.active;
|
||||
var keys = Object.keys(JOINT_PARENT_MAP);
|
||||
var i, l = keys.length;
|
||||
var absDefaultRot = {};
|
||||
var jointName, channel, pose, parentJointName, j, parentDefaultAbsRot;
|
||||
var localRotations = {};
|
||||
var localTranslations = {};
|
||||
for (i = 0; i < l; i++) {
|
||||
var jointName = keys[i];
|
||||
var channel = Controller.Hardware.Neuron[jointName];
|
||||
if (channel) {
|
||||
pose = Controller.getPoseValue(channel);
|
||||
parentJointName = JOINT_PARENT_MAP[jointName];
|
||||
j = MyAvatar.getJointIndex(jointName);
|
||||
defaultAbsRot = this._defaultAbsRotMap[jointName];
|
||||
parentDefaultAbsRot = this._defaultAbsRotMap[parentJointName];
|
||||
|
||||
// Rotations from the neuron controller are in world orientation but are delta's from the default pose.
|
||||
// So first we build the absolute rotation of the default pose (local into world).
|
||||
// Then apply the rotation from the controller, in world space.
|
||||
// Then we transform back into joint local by multiplying by the inverse of the parents absolute rotation.
|
||||
var localRotation = Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot));
|
||||
if (!hmdActive || jointName !== "Hips") {
|
||||
MyAvatar.setJointRotation(j, localRotation);
|
||||
}
|
||||
localRotations[jointName] = localRotation;
|
||||
|
||||
// translation proportions might be different from the neuron avatar and the user avatar skeleton.
|
||||
// so this is disabled by default
|
||||
if (USE_TRANSLATIONS) {
|
||||
var localTranslation = Vec3.multiplyQbyV(Quat.inverse(parentDefaultAbsRot), pose.translation);
|
||||
MyAvatar.setJointTranslation(j, localTranslation);
|
||||
localTranslations[jointName] = localTranslation;
|
||||
} else {
|
||||
localTranslations[jointName] = MyAvatar.getDefaultJointTranslation(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it attempts to adjust the hips so that the avatar's head is at the same location & oreintation as the HMD.
|
||||
// however it's fighting with the internal c++ code that also attempts to adjust the hips.
|
||||
if (hmdActive) {
|
||||
var UNIT_SCALE = 1 / 100;
|
||||
var hmdXform = new Xform(HMD.orientation, Vec3.multiply(1 / UNIT_SCALE, HMD.position)); // convert to cm
|
||||
var y180Xform = new Xform({x: 0, y: 1, z: 0, w: 0}, {x: 0, y: 0, z: 0});
|
||||
var avatarXform = new Xform(MyAvatar.orientation, Vec3.multiply(1 / UNIT_SCALE, MyAvatar.position)); // convert to cm
|
||||
var hipsJointIndex = MyAvatar.getJointIndex("Hips");
|
||||
var modelOffsetInvXform = new Xform({x: 0, y: 0, z: 0, w: 1}, MyAvatar.getDefaultJointTranslation(hipsJointIndex));
|
||||
var defaultHipsXform = new Xform(MyAvatar.getDefaultJointRotation(hipsJointIndex), MyAvatar.getDefaultJointTranslation(hipsJointIndex));
|
||||
|
||||
var headXform = new Xform(localRotations["Head"], localTranslations["Head"]);
|
||||
|
||||
// transform eyes down the heirarchy chain into avatar space.
|
||||
var hierarchy = ["Neck", "Spine3", "Spine2", "Spine1", "Spine"];
|
||||
var i, l = hierarchy.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
var xform = new Xform(localRotations[hierarchy[i]], localTranslations[hierarchy[i]]);
|
||||
headXform = Xform.mul(xform, headXform);
|
||||
}
|
||||
headXform = Xform.mul(defaultHipsXform, headXform);
|
||||
|
||||
var preXform = Xform.mul(headXform, y180Xform);
|
||||
var postXform = Xform.mul(avatarXform, Xform.mul(y180Xform, modelOffsetInvXform.inv()));
|
||||
|
||||
// solve for the offset that will put the eyes at the hmd position & orientation.
|
||||
var hipsOffsetXform = Xform.mul(postXform.inv(), Xform.mul(hmdXform, preXform.inv()));
|
||||
|
||||
// now combine it with the default hips transform
|
||||
var hipsXform = Xform.mul(hipsOffsetXform, defaultHipsXform);
|
||||
|
||||
MyAvatar.setJointRotation("Hips", hipsXform.rot);
|
||||
MyAvatar.setJointTranslation("Hips", hipsXform.pos);
|
||||
}
|
||||
};
|
||||
|
||||
var neuronAvatar = new NeuronAvatar();
|
||||
|
||||
function updateCallback(deltaTime) {
|
||||
neuronAvatar.update(deltaTime);
|
||||
}
|
||||
|
|
@ -22,33 +22,13 @@ function length(posA, posB) {
|
|||
return length;
|
||||
}
|
||||
|
||||
var EXPECTED_CHANGE = 50;
|
||||
var lastPos = Controller.getReticlePosition();
|
||||
function moveReticleAbsolute(x, y) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
var dX = x - globalPos.x;
|
||||
var dY = y - globalPos.y;
|
||||
|
||||
// some debugging to see if position is jumping around on us...
|
||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||
debugPrint("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||
}
|
||||
|
||||
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||
debugPrint("surpressing unexpectedly large change dX:" + dX + "----------------------------");
|
||||
}
|
||||
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||
debugPrint("surpressing unexpectedly large change dY:" + dY + "----------------------------");
|
||||
}
|
||||
|
||||
globalPos.x = x;
|
||||
globalPos.y = y;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
lastPos = globalPos;
|
||||
}
|
||||
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||
|
@ -56,8 +36,6 @@ mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.A
|
|||
mapping.enable();
|
||||
|
||||
|
||||
var lastRotatedLeft = Vec3.UNIT_NEG_Y;
|
||||
var lastRotatedRight = Vec3.UNIT_NEG_Y;
|
||||
|
||||
function debugPrint(message) {
|
||||
if (DEBUGGING) {
|
||||
|
@ -65,24 +43,9 @@ function debugPrint(message) {
|
|||
}
|
||||
}
|
||||
|
||||
var MAX_WAKE_UP_DISTANCE = 0.005;
|
||||
var MIN_WAKE_UP_DISTANCE = 0.001;
|
||||
var INITIAL_WAKE_UP_DISTANCE = MIN_WAKE_UP_DISTANCE;
|
||||
var INCREMENTAL_WAKE_UP_DISTANCE = 0.001;
|
||||
|
||||
var MAX_SLEEP_DISTANCE = 0.0004;
|
||||
var MIN_SLEEP_DISTANCE = 0.00001; //0.00002;
|
||||
var INITIAL_SLEEP_DISTANCE = MIN_SLEEP_DISTANCE;
|
||||
var INCREMENTAL_SLEEP_DISTANCE = 0.000002; // 0.00002;
|
||||
|
||||
var leftAsleep = true;
|
||||
var rightAsleep = true;
|
||||
|
||||
var leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
var rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
|
||||
var leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
var rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
var leftRightBias = 0.0;
|
||||
var filteredRotatedLeft = Vec3.UNIT_NEG_Y;
|
||||
var filteredRotatedRight = Vec3.UNIT_NEG_Y;
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
|
||||
|
@ -96,153 +59,46 @@ Script.update.connect(function(deltaTime) {
|
|||
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
|
||||
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
|
||||
|
||||
var suppressRight = false;
|
||||
var suppressLeft = false;
|
||||
|
||||
// What I really want to do is to slowly increase the epsilon you have to move it
|
||||
// to wake up, the longer you go without moving it
|
||||
var leftDistance = Vec3.distance(rotatedLeft, lastRotatedLeft);
|
||||
var rightDistance = Vec3.distance(rotatedRight, lastRotatedRight);
|
||||
|
||||
// check to see if hand should wakeup or sleep
|
||||
if (leftAsleep) {
|
||||
if (leftDistance > leftWakeUpDistance) {
|
||||
leftAsleep = false;
|
||||
leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// grow the wake up distance to make it harder to wake up
|
||||
leftWakeUpDistance = Math.min(leftWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE);
|
||||
}
|
||||
} else {
|
||||
// we are awake, determine if we should fall asleep, if we haven't moved
|
||||
// at least as much as our sleep distance then we sleep
|
||||
if (leftDistance < leftSleepDistance) {
|
||||
leftAsleep = true;
|
||||
leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// if we moved more than the sleep amount, but we moved less than the max sleep
|
||||
// amount, then increase our liklihood of sleep.
|
||||
if (leftDistance < MAX_SLEEP_DISTANCE) {
|
||||
print("growing sleep....");
|
||||
leftSleepDistance = Math.max(leftSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE);
|
||||
} else {
|
||||
// otherwise reset it to initial
|
||||
leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (leftAsleep) {
|
||||
suppressLeft = true;
|
||||
debugPrint("suppressing left not moving enough");
|
||||
}
|
||||
|
||||
// check to see if hand should wakeup or sleep
|
||||
if (rightAsleep) {
|
||||
if (rightDistance > rightWakeUpDistance) {
|
||||
rightAsleep = false;
|
||||
rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// grow the wake up distance to make it harder to wake up
|
||||
rightWakeUpDistance = Math.min(rightWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE);
|
||||
}
|
||||
} else {
|
||||
// we are awake, determine if we should fall asleep, if we haven't moved
|
||||
// at least as much as our sleep distance then we sleep
|
||||
if (rightDistance < rightSleepDistance) {
|
||||
rightAsleep = true;
|
||||
rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// if we moved more than the sleep amount, but we moved less than the max sleep
|
||||
// amount, then increase our liklihood of sleep.
|
||||
if (rightDistance < MAX_SLEEP_DISTANCE) {
|
||||
print("growing sleep....");
|
||||
rightSleepDistance = Math.max(rightSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE);
|
||||
} else {
|
||||
// otherwise reset it to initial
|
||||
rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rightAsleep) {
|
||||
suppressRight = true;
|
||||
debugPrint("suppressing right not moving enough");
|
||||
}
|
||||
|
||||
// check to see if hand is on base station
|
||||
if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) {
|
||||
suppressLeft = true;
|
||||
debugPrint("suppressing left on base station");
|
||||
}
|
||||
if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) {
|
||||
suppressRight = true;
|
||||
debugPrint("suppressing right on base station");
|
||||
}
|
||||
|
||||
// Keep track of last rotations, to detect resting (but not on base station hands) in the future
|
||||
lastRotatedLeft = rotatedLeft;
|
||||
lastRotatedRight = rotatedRight;
|
||||
|
||||
if (suppressLeft && suppressRight) {
|
||||
debugPrint("both hands suppressed bail out early");
|
||||
return;
|
||||
|
||||
// Decide which hand should be controlling the pointer
|
||||
// by comparing which one is moving more, and by
|
||||
// tending to stay with the one moving more.
|
||||
var BIAS_ADJUST_RATE = 0.5;
|
||||
var BIAS_ADJUST_DEADZONE = 0.05;
|
||||
leftRightBias += (Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity)) * BIAS_ADJUST_RATE;
|
||||
if (leftRightBias < BIAS_ADJUST_DEADZONE) {
|
||||
leftRightBias = 0.0;
|
||||
} else if (leftRightBias > (1.0 - BIAS_ADJUST_DEADZONE)) {
|
||||
leftRightBias = 1.0;
|
||||
}
|
||||
|
||||
if (suppressLeft) {
|
||||
debugPrint("right only");
|
||||
rotatedLeft = rotatedRight;
|
||||
}
|
||||
if (suppressRight) {
|
||||
debugPrint("left only");
|
||||
rotatedRight = rotatedLeft;
|
||||
}
|
||||
|
||||
// Average the two hand positions, if either hand is on base station, the
|
||||
// other hand becomes the only used hand and the average is the hand in use
|
||||
var rotated = Vec3.multiply(Vec3.sum(rotatedRight,rotatedLeft), 0.5);
|
||||
|
||||
if (DEBUGGING) {
|
||||
Vec3.print("rotatedRight:", rotatedRight);
|
||||
Vec3.print("rotatedLeft:", rotatedLeft);
|
||||
Vec3.print("rotated:", rotated);
|
||||
}
|
||||
// Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers
|
||||
var VELOCITY_FILTER_GAIN = 1.0;
|
||||
filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
|
||||
filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
|
||||
var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, leftRightBias);
|
||||
|
||||
var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again...
|
||||
var absoluteYaw = -rotated.x; // from -1 left to 1 right
|
||||
|
||||
if (DEBUGGING) {
|
||||
print("absolutePitch:" + absolutePitch);
|
||||
print("absoluteYaw:" + absoluteYaw);
|
||||
Vec3.print("rotated:", rotated);
|
||||
}
|
||||
|
||||
var ROTATION_BOUND = 0.6;
|
||||
var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND);
|
||||
var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND);
|
||||
if (DEBUGGING) {
|
||||
print("clampYaw:" + clampYaw);
|
||||
print("clampPitch:" + clampPitch);
|
||||
}
|
||||
|
||||
// using only from -ROTATION_BOUND to ROTATION_BOUND
|
||||
var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
|
||||
if (DEBUGGING) {
|
||||
print("xRatio:" + xRatio);
|
||||
print("yRatio:" + yRatio);
|
||||
}
|
||||
|
||||
var x = screenSizeX * xRatio;
|
||||
var y = screenSizeY * yRatio;
|
||||
|
||||
if (DEBUGGING) {
|
||||
print("position x:" + x + " y:" + y);
|
||||
}
|
||||
if (!(xRatio == 0.5 && yRatio == 0)) {
|
||||
// don't move the reticle with the hand controllers unless the controllers are actually being moved
|
||||
var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.0001;
|
||||
var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - leftRightBias) + Vec3.length(poseRight.angularVelocity) * leftRightBias;
|
||||
|
||||
if (!(xRatio == 0.5 && yRatio == 0) && (angularVelocityMagnitude > MINIMUM_CONTROLLER_ANGULAR_VELOCITY)) {
|
||||
moveReticleAbsolute(x, y);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -29,21 +29,14 @@
|
|||
this.equipped = false;
|
||||
this.forceMultiplier = 1;
|
||||
this.laserLength = 100;
|
||||
this.laserOffsets = {
|
||||
y: .095
|
||||
};
|
||||
this.firingOffsets = {
|
||||
z: 0.16
|
||||
}
|
||||
|
||||
this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw");
|
||||
this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav");
|
||||
this.playRichochetSoundChance = 0.1;
|
||||
this.fireVolume = 0.2;
|
||||
this.bulletForce = 10;
|
||||
|
||||
|
||||
|
||||
this.showLaser = false;
|
||||
|
||||
};
|
||||
|
||||
Pistol.prototype = {
|
||||
|
@ -58,20 +51,36 @@
|
|||
if (!this.equipped) {
|
||||
return;
|
||||
}
|
||||
this.toggleWithTriggerPressure();
|
||||
this.updateProps();
|
||||
if (this.showLaser) {
|
||||
this.updateLaser();
|
||||
}
|
||||
this.toggleWithTriggerPressure();
|
||||
|
||||
|
||||
},
|
||||
|
||||
updateProps: function() {
|
||||
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
this.position = gunProps.position;
|
||||
this.rotation = gunProps.rotation;
|
||||
this.firingDirection = Quat.getFront(this.rotation);
|
||||
var upVec = Quat.getUp(this.rotation);
|
||||
this.barrelPoint = Vec3.sum(this.position, Vec3.multiply(upVec, this.laserOffsets.y));
|
||||
this.laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength));
|
||||
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
|
||||
var pickRay = {
|
||||
origin: this.barrelPoint,
|
||||
direction: this.firingDirection
|
||||
};
|
||||
},
|
||||
toggleWithTriggerPressure: function() {
|
||||
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]);
|
||||
|
||||
if (this.triggerValue < RELOAD_THRESHOLD) {
|
||||
// print('RELOAD');
|
||||
this.canShoot = true;
|
||||
}
|
||||
if (this.canShoot === true && this.triggerValue === 1) {
|
||||
// print('SHOOT');
|
||||
this.fire();
|
||||
this.canShoot = false;
|
||||
}
|
||||
|
@ -91,17 +100,10 @@
|
|||
|
||||
},
|
||||
updateLaser: function() {
|
||||
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var position = gunProps.position;
|
||||
var rotation = gunProps.rotation;
|
||||
this.firingDirection = Quat.getFront(rotation);
|
||||
var upVec = Quat.getUp(rotation);
|
||||
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
|
||||
var laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength));
|
||||
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
|
||||
|
||||
Overlays.editOverlay(this.laser, {
|
||||
start: this.barrelPoint,
|
||||
end: laserTip,
|
||||
end: this.laserTip,
|
||||
alpha: 1
|
||||
});
|
||||
},
|
||||
|
@ -114,19 +116,6 @@
|
|||
});
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
// this.initControllerMapping();
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
color: COLORS.RED,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
lineWidth: 2
|
||||
});
|
||||
},
|
||||
|
||||
triggerPress: function(hand, value) {
|
||||
if (this.hand === hand && value === 1) {
|
||||
//We are pulling trigger on the hand we have the gun in, so fire
|
||||
|
@ -135,15 +124,16 @@
|
|||
},
|
||||
|
||||
fire: function() {
|
||||
var pickRay = {
|
||||
origin: this.barrelPoint,
|
||||
direction: this.firingDirection
|
||||
};
|
||||
|
||||
Audio.playSound(this.fireSound, {
|
||||
position: this.barrelPoint,
|
||||
volume: this.fireVolume
|
||||
});
|
||||
|
||||
var pickRay = {
|
||||
origin: this.barrelPoint,
|
||||
direction: this.firingDirection
|
||||
};
|
||||
this.createGunFireEffect(this.barrelPoint)
|
||||
var intersection = Entities.findRayIntersectionBlocking(pickRay, true);
|
||||
if (intersection.intersects) {
|
||||
|
@ -170,11 +160,11 @@
|
|||
},
|
||||
|
||||
createEntityHitEffect: function(position) {
|
||||
var flash = Entities.addEntity({
|
||||
var sparks = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
position: position,
|
||||
lifetime: 4,
|
||||
"name": "Flash Emitter",
|
||||
"name": "Sparks Emitter",
|
||||
"color": {
|
||||
red: 228,
|
||||
green: 128,
|
||||
|
@ -228,7 +218,7 @@
|
|||
});
|
||||
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(flash, {
|
||||
Entities.editEntity(sparks, {
|
||||
isEmitting: false
|
||||
});
|
||||
}, 100);
|
||||
|
@ -261,11 +251,11 @@
|
|||
"z": 0
|
||||
},
|
||||
"accelerationSpread": {
|
||||
"x": .2,
|
||||
"x": 0.2,
|
||||
"y": 0,
|
||||
"z": .2
|
||||
"z": 0.2
|
||||
},
|
||||
"radiusSpread": .04,
|
||||
"radiusSpread": 0.04,
|
||||
"particleRadius": 0.07,
|
||||
"radiusStart": 0.07,
|
||||
"radiusFinish": 0.07,
|
||||
|
@ -282,11 +272,46 @@
|
|||
});
|
||||
}, 100);
|
||||
|
||||
var flash = Entities.addEntity({
|
||||
Entities.editEntity(this.flash, {
|
||||
isEmitting: true
|
||||
});
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(_this.flash, {
|
||||
isEmitting: false
|
||||
});
|
||||
}, 100)
|
||||
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
color: COLORS.RED,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
lineWidth: 2
|
||||
});
|
||||
this.laserOffsets = {
|
||||
y: 0.095
|
||||
};
|
||||
this.firingOffsets = {
|
||||
z: 0.16
|
||||
}
|
||||
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var position = gunProps.position;
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
this.firingDirection = Quat.getFront(rotation);
|
||||
var upVec = Quat.getUp(rotation);
|
||||
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
|
||||
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
|
||||
|
||||
this.flash = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
position: position,
|
||||
lifetime: 4,
|
||||
position: this.barrelPoint,
|
||||
"name": "Muzzle Flash",
|
||||
isEmitting: false,
|
||||
"color": {
|
||||
red: 228,
|
||||
green: 128,
|
||||
|
@ -339,16 +364,13 @@
|
|||
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
|
||||
});
|
||||
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(flash, {
|
||||
isEmitting: false
|
||||
});
|
||||
}, 100)
|
||||
|
||||
}
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(_this.flash, {parentID: _this.entityID});
|
||||
}, 500)
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Pistol();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -109,7 +109,9 @@ add_dependency_external_projects(sdl2)
|
|||
if (WIN32)
|
||||
add_dependency_external_projects(OpenVR)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR APPLE)
|
||||
add_dependency_external_projects(neuron)
|
||||
endif()
|
||||
|
||||
# disable /OPT:REF and /OPT:ICF for the Debug builds
|
||||
# This will prevent the following linker warnings
|
||||
|
@ -209,6 +211,9 @@ else (APPLE)
|
|||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources"
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/resources
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/examples"
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/scripts
|
||||
)
|
||||
|
||||
# link target to external libraries
|
||||
|
|
7
interface/resources/controllers/neuron.json
Normal file
7
interface/resources/controllers/neuron.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "Neuron to Standard",
|
||||
"channels": [
|
||||
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
|
||||
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
|
||||
]
|
||||
}
|
|
@ -3489,78 +3489,86 @@ namespace render {
|
|||
|
||||
// Background rendering decision
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) {
|
||||
auto backgroundMode = skyStage->getBackgroundMode();
|
||||
|
||||
if (backgroundMode == model::SunSkyStage::NO_BACKGROUND) {
|
||||
// this line intentionally left blank
|
||||
} else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
PerformanceTimer perfTimer("stars");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::payloadRender<BackgroundRenderData>() ... stars...");
|
||||
// should be the first rendering pass - w/o depth buffer / lighting
|
||||
|
||||
// compute starfield alpha based on distance from atmosphere
|
||||
float alpha = 1.0f;
|
||||
bool hasStars = true;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
// TODO: handle this correctly for zones
|
||||
const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum
|
||||
|
||||
if (closestData.getHasStars()) {
|
||||
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
|
||||
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
|
||||
|
||||
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
|
||||
/ closestData.getAtmosphereOuterRadius();
|
||||
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
|
||||
if (height < closestData.getAtmosphereInnerRadius()) {
|
||||
// If we're inside the atmosphere, then determine if our keyLight is below the horizon
|
||||
alpha = 0.0f;
|
||||
|
||||
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
|
||||
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
|
||||
}
|
||||
|
||||
|
||||
} else if (height < closestData.getAtmosphereOuterRadius()) {
|
||||
alpha = (height - closestData.getAtmosphereInnerRadius()) /
|
||||
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
|
||||
|
||||
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
|
||||
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasStars = false;
|
||||
}
|
||||
} else {
|
||||
if (backgroundMode == model::SunSkyStage::SKY_BOX) {
|
||||
auto skybox = skyStage->getSkybox();
|
||||
if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) {
|
||||
PerformanceTimer perfTimer("skybox");
|
||||
skybox->render(batch, *(args->_viewFrustum));
|
||||
} else {
|
||||
// If no skybox texture is available, render the SKY_DOME while it loads
|
||||
backgroundMode = model::SunSkyStage::SKY_DOME;
|
||||
}
|
||||
|
||||
// finally render the starfield
|
||||
if (hasStars) {
|
||||
background->_stars.render(args, alpha);
|
||||
}
|
||||
|
||||
// draw the sky dome
|
||||
if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
PerformanceTimer perfTimer("atmosphere");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... atmosphere...");
|
||||
|
||||
background->_environment->renderAtmospheres(batch, *(args->_viewFrustum));
|
||||
}
|
||||
|
||||
}
|
||||
} else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) {
|
||||
PerformanceTimer perfTimer("skybox");
|
||||
auto skybox = skyStage->getSkybox();
|
||||
if (skybox) {
|
||||
skybox->render(batch, *(args->_viewFrustum));
|
||||
if (backgroundMode == model::SunSkyStage::SKY_DOME) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
PerformanceTimer perfTimer("stars");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::payloadRender<BackgroundRenderData>() ... stars...");
|
||||
// should be the first rendering pass - w/o depth buffer / lighting
|
||||
|
||||
// compute starfield alpha based on distance from atmosphere
|
||||
float alpha = 1.0f;
|
||||
bool hasStars = true;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
// TODO: handle this correctly for zones
|
||||
const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum
|
||||
|
||||
if (closestData.getHasStars()) {
|
||||
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
|
||||
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
|
||||
|
||||
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
|
||||
/ closestData.getAtmosphereOuterRadius();
|
||||
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
|
||||
if (height < closestData.getAtmosphereInnerRadius()) {
|
||||
// If we're inside the atmosphere, then determine if our keyLight is below the horizon
|
||||
alpha = 0.0f;
|
||||
|
||||
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
|
||||
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
|
||||
}
|
||||
|
||||
|
||||
} else if (height < closestData.getAtmosphereOuterRadius()) {
|
||||
alpha = (height - closestData.getAtmosphereInnerRadius()) /
|
||||
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
|
||||
|
||||
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
|
||||
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasStars = false;
|
||||
}
|
||||
}
|
||||
|
||||
// finally render the starfield
|
||||
if (hasStars) {
|
||||
background->_stars.render(args, alpha);
|
||||
}
|
||||
|
||||
// draw the sky dome
|
||||
if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
PerformanceTimer perfTimer("atmosphere");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... atmosphere...");
|
||||
|
||||
background->_environment->renderAtmospheres(batch, *(args->_viewFrustum));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,8 +310,8 @@ QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
|
|||
}
|
||||
|
||||
AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
|
||||
QReadLocker locker(&_hashLock);
|
||||
return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar.
|
||||
// Null/Default-constructed QUuids will return MyAvatar
|
||||
return getAvatarBySessionID(avatarID).get();
|
||||
}
|
||||
|
||||
|
||||
|
|
676
libraries/animation/src/AnimExpression.cpp
Normal file
676
libraries/animation/src/AnimExpression.cpp
Normal file
|
@ -0,0 +1,676 @@
|
|||
//
|
||||
// AnimExpression.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on 11/1/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <StreamUtils.h>
|
||||
#include <QRegExp>
|
||||
|
||||
#include "AnimExpression.h"
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
AnimExpression::AnimExpression(const QString& str) :
|
||||
_expression(str) {
|
||||
auto iter = str.begin();
|
||||
parseExpr(_expression, iter);
|
||||
while(!_tokenStack.empty()) {
|
||||
_tokenStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Tokenizer
|
||||
//
|
||||
|
||||
void AnimExpression::unconsumeToken(const Token& token) {
|
||||
_tokenStack.push(token);
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const {
|
||||
if (!_tokenStack.empty()) {
|
||||
Token top = _tokenStack.top();
|
||||
_tokenStack.pop();
|
||||
return top;
|
||||
} else {
|
||||
while (iter != str.end()) {
|
||||
if (iter->isSpace()) {
|
||||
++iter;
|
||||
} else if (iter->isLetter()) {
|
||||
return consumeIdentifier(str, iter);
|
||||
} else if (iter->isDigit()) {
|
||||
return consumeNumber(str, iter);
|
||||
} else {
|
||||
switch (iter->unicode()) {
|
||||
case '&': return consumeAnd(str, iter);
|
||||
case '|': return consumeOr(str, iter);
|
||||
case '>': return consumeGreaterThan(str, iter);
|
||||
case '<': return consumeLessThan(str, iter);
|
||||
case '(': ++iter; return Token(Token::LeftParen);
|
||||
case ')': ++iter; return Token(Token::RightParen);
|
||||
case '!': return consumeNot(str, iter);
|
||||
case '-': ++iter; return Token(Token::Minus);
|
||||
case '+': ++iter; return Token(Token::Plus);
|
||||
case '*': ++iter; return Token(Token::Multiply);
|
||||
case '/': ++iter; return Token(Token::Divide);
|
||||
case '%': ++iter; return Token(Token::Modulus);
|
||||
case ',': ++iter; return Token(Token::Comma);
|
||||
default:
|
||||
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
|
||||
return Token(Token::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Token(Token::End);
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->isLetter());
|
||||
auto begin = iter;
|
||||
while ((iter->isLetter() || iter->isDigit()) && iter != str.end()) {
|
||||
++iter;
|
||||
}
|
||||
int pos = (int)(begin - str.begin());
|
||||
int len = (int)(iter - begin);
|
||||
|
||||
QStringRef stringRef(const_cast<const QString*>(&str), pos, len);
|
||||
if (stringRef == "true") {
|
||||
return Token(true);
|
||||
} else if (stringRef == "false") {
|
||||
return Token(false);
|
||||
} else {
|
||||
return Token(stringRef);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: not very efficient or accruate, but it's close enough for now.
|
||||
static float computeFractionalPart(int fractionalPart)
|
||||
{
|
||||
float frac = (float)fractionalPart;
|
||||
while (fractionalPart) {
|
||||
fractionalPart /= 10;
|
||||
frac /= 10.0f;
|
||||
}
|
||||
return frac;
|
||||
}
|
||||
|
||||
static float computeFloat(int whole, int fraction) {
|
||||
return (float)whole + computeFractionalPart(fraction);
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->isDigit());
|
||||
auto begin = iter;
|
||||
while (iter->isDigit() && iter != str.end()) {
|
||||
++iter;
|
||||
}
|
||||
|
||||
// parse whole integer part
|
||||
int pos = (int)(begin - str.begin());
|
||||
int len = (int)(iter - begin);
|
||||
QString sub = QStringRef(const_cast<const QString*>(&str), pos, len).toString();
|
||||
int whole = sub.toInt();
|
||||
|
||||
// parse optional fractional part
|
||||
if (iter->unicode() == '.') {
|
||||
iter++;
|
||||
auto begin = iter;
|
||||
while (iter->isDigit() && iter != str.end()) {
|
||||
++iter;
|
||||
}
|
||||
|
||||
int pos = (int)(begin - str.begin());
|
||||
int len = (int)(iter - begin);
|
||||
QString sub = QStringRef(const_cast<const QString*>(&str), pos, len).toString();
|
||||
int fraction = sub.toInt();
|
||||
|
||||
return Token(computeFloat(whole, fraction));
|
||||
|
||||
} else {
|
||||
return Token(whole);
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->unicode() == '&');
|
||||
iter++;
|
||||
if (iter->unicode() == '&') {
|
||||
iter++;
|
||||
return Token(Token::And);
|
||||
} else {
|
||||
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
|
||||
return Token(Token::Error);
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeOr(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->unicode() == '|');
|
||||
iter++;
|
||||
if (iter->unicode() == '|') {
|
||||
iter++;
|
||||
return Token(Token::Or);
|
||||
} else {
|
||||
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
|
||||
return Token(Token::Error);
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeGreaterThan(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->unicode() == '>');
|
||||
iter++;
|
||||
if (iter->unicode() == '=') {
|
||||
iter++;
|
||||
return Token(Token::GreaterThanEqual);
|
||||
} else {
|
||||
return Token(Token::GreaterThan);
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeLessThan(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->unicode() == '<');
|
||||
iter++;
|
||||
if (iter->unicode() == '=') {
|
||||
iter++;
|
||||
return Token(Token::LessThanEqual);
|
||||
} else {
|
||||
return Token(Token::LessThan);
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::const_iterator& iter) const {
|
||||
assert(iter != str.end());
|
||||
assert(iter->unicode() == '!');
|
||||
iter++;
|
||||
if (iter->unicode() == '=') {
|
||||
iter++;
|
||||
return Token(Token::NotEqual);
|
||||
} else {
|
||||
return Token(Token::Not);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Parser
|
||||
//
|
||||
|
||||
/*
|
||||
Expr → Term Expr'
|
||||
Expr' → '||' Term Expr'
|
||||
| ε
|
||||
Term → Unary Term'
|
||||
Term' → '&&' Unary Term'
|
||||
| ε
|
||||
Unary → '!' Unary
|
||||
| Factor
|
||||
Factor → INT
|
||||
| BOOL
|
||||
| FLOAT
|
||||
| IDENTIFIER
|
||||
| '(' Expr ')'
|
||||
*/
|
||||
|
||||
// Expr → Term Expr'
|
||||
bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseExprPrime(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expr' → '||' Term Expr' | ε
|
||||
bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Or) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
if (!parseExprPrime(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::Or});
|
||||
return true;
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Term → Unary Term'
|
||||
bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) {
|
||||
if (!parseUnary(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseTermPrime(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Term' → '&&' Unary Term' | ε
|
||||
bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::And) {
|
||||
if (!parseUnary(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
if (!parseTermPrime(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::And});
|
||||
return true;
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Unary → '!' Unary | Factor
|
||||
bool AnimExpression::parseUnary(const QString& str, QString::const_iterator& iter) {
|
||||
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Not) {
|
||||
if (!parseUnary(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::Not});
|
||||
return true;
|
||||
}
|
||||
unconsumeToken(token);
|
||||
|
||||
return parseFactor(str, iter);
|
||||
}
|
||||
|
||||
|
||||
// Factor → INT | BOOL | FLOAT | IDENTIFIER | '(' Expr ')'
|
||||
bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Int) {
|
||||
_opCodes.push_back(OpCode {token.intVal});
|
||||
return true;
|
||||
} else if (token.type == Token::Bool) {
|
||||
_opCodes.push_back(OpCode {(bool)token.intVal});
|
||||
return true;
|
||||
} else if (token.type == Token::Float) {
|
||||
_opCodes.push_back(OpCode {token.floatVal});
|
||||
return true;
|
||||
} else if (token.type == Token::Identifier) {
|
||||
_opCodes.push_back(OpCode {token.strVal});
|
||||
return true;
|
||||
} else if (token.type == Token::LeftParen) {
|
||||
if (!parseExpr(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
auto nextToken = consumeToken(str, iter);
|
||||
if (nextToken.type != Token::RightParen) {
|
||||
unconsumeToken(nextToken);
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Evaluator
|
||||
//
|
||||
|
||||
AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const {
|
||||
std::stack<OpCode> stack;
|
||||
for (auto& opCode : _opCodes) {
|
||||
switch (opCode.type) {
|
||||
case OpCode::Identifier:
|
||||
case OpCode::Int:
|
||||
case OpCode::Float:
|
||||
case OpCode::Bool:
|
||||
stack.push(opCode);
|
||||
break;
|
||||
case OpCode::And: evalAnd(map, stack); break;
|
||||
case OpCode::Or: evalOr(map, stack); break;
|
||||
case OpCode::GreaterThan: evalGreaterThan(map, stack); break;
|
||||
case OpCode::GreaterThanEqual: evalGreaterThanEqual(map, stack); break;
|
||||
case OpCode::LessThan: evalLessThan(map, stack); break;
|
||||
case OpCode::LessThanEqual: evalLessThanEqual(map, stack); break;
|
||||
case OpCode::Equal: evalEqual(map, stack); break;
|
||||
case OpCode::NotEqual: evalNotEqual(map, stack); break;
|
||||
case OpCode::Not: evalNot(map, stack); break;
|
||||
case OpCode::Subtract: evalSubtract(map, stack); break;
|
||||
case OpCode::Add: evalAdd(map, stack); break;
|
||||
case OpCode::Multiply: evalMultiply(map, stack); break;
|
||||
case OpCode::Divide: evalDivide(map, stack); break;
|
||||
case OpCode::Modulus: evalModulus(map, stack); break;
|
||||
case OpCode::UnaryMinus: evalUnaryMinus(map, stack); break;
|
||||
}
|
||||
}
|
||||
return coerseToValue(map, stack.top());
|
||||
}
|
||||
|
||||
#define POP_BOOL(NAME) \
|
||||
const OpCode& NAME##_temp = stack.top(); \
|
||||
bool NAME = NAME##_temp.coerceBool(map); \
|
||||
stack.pop()
|
||||
|
||||
#define PUSH(EXPR) \
|
||||
stack.push(OpCode {(EXPR)})
|
||||
|
||||
void AnimExpression::evalAnd(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
POP_BOOL(lhs);
|
||||
POP_BOOL(rhs);
|
||||
PUSH(lhs && rhs);
|
||||
}
|
||||
|
||||
void AnimExpression::evalOr(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
POP_BOOL(lhs);
|
||||
POP_BOOL(rhs);
|
||||
PUSH(lhs || rhs);
|
||||
}
|
||||
|
||||
void AnimExpression::evalGreaterThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(false);
|
||||
}
|
||||
|
||||
void AnimExpression::evalGreaterThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(false);
|
||||
}
|
||||
|
||||
void AnimExpression::evalLessThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(false);
|
||||
}
|
||||
|
||||
void AnimExpression::evalLessThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(false);
|
||||
}
|
||||
|
||||
void AnimExpression::evalEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(false);
|
||||
}
|
||||
|
||||
void AnimExpression::evalNotEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(false);
|
||||
}
|
||||
|
||||
void AnimExpression::evalNot(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
POP_BOOL(rhs);
|
||||
PUSH(!rhs);
|
||||
}
|
||||
|
||||
void AnimExpression::evalSubtract(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(0.0f);
|
||||
}
|
||||
|
||||
void AnimExpression::add(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs + rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH((float)lhs + rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::add(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs + (float)rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH(lhs + rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
OpCode rhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
|
||||
switch (lhs.type) {
|
||||
case OpCode::Bool:
|
||||
add(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Int:
|
||||
add(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
add(lhs.floatVal, rhs, stack);
|
||||
break;
|
||||
default:
|
||||
add(0, rhs, stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::evalMultiply(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
OpCode rhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
|
||||
switch(lhs.type) {
|
||||
case OpCode::Bool:
|
||||
mul(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Int:
|
||||
mul(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
mul(lhs.floatVal, rhs, stack);
|
||||
break;
|
||||
default:
|
||||
mul(0, rhs, stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::mul(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs * rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH((float)lhs * rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::mul(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs * (float)rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH(lhs * rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::evalDivide(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH(0.0f);
|
||||
}
|
||||
|
||||
void AnimExpression::evalModulus(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
// TODO:
|
||||
PUSH((int)0);
|
||||
}
|
||||
|
||||
void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
switch (rhs.type) {
|
||||
case OpCode::Identifier: {
|
||||
const AnimVariant& var = map.get(rhs.strVal);
|
||||
switch (var.getType()) {
|
||||
case AnimVariant::Type::Bool:
|
||||
qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool";
|
||||
// interpret this as boolean not.
|
||||
PUSH(!var.getBool());
|
||||
break;
|
||||
case AnimVariant::Type::Int:
|
||||
PUSH(-var.getInt());
|
||||
break;
|
||||
case AnimVariant::Type::Float:
|
||||
PUSH(-var.getFloat());
|
||||
break;
|
||||
default:
|
||||
// TODO: Vec3, Quat are unsupported
|
||||
assert(false);
|
||||
PUSH(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case OpCode::Int:
|
||||
PUSH(-rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH(-rhs.floatVal);
|
||||
break;
|
||||
case OpCode::Bool:
|
||||
qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool";
|
||||
// interpret this as boolean not.
|
||||
PUSH(!rhs.coerceBool(map));
|
||||
break;
|
||||
default:
|
||||
qCCritical(animation) << "AnimExpression: ERRROR for unary minus, expected a number, type = " << rhs.type;
|
||||
assert(false);
|
||||
PUSH(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const {
|
||||
switch (opCode.type) {
|
||||
case OpCode::Identifier:
|
||||
{
|
||||
const AnimVariant& var = map.get(opCode.strVal);
|
||||
switch (var.getType()) {
|
||||
case AnimVariant::Type::Bool:
|
||||
return OpCode((bool)var.getBool());
|
||||
break;
|
||||
case AnimVariant::Type::Int:
|
||||
return OpCode(var.getInt());
|
||||
break;
|
||||
case AnimVariant::Type::Float:
|
||||
return OpCode(var.getFloat());
|
||||
break;
|
||||
default:
|
||||
// TODO: Vec3, Quat are unsupported
|
||||
assert(false);
|
||||
return OpCode(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
case OpCode::Float:
|
||||
return opCode;
|
||||
default:
|
||||
qCCritical(animation) << "AnimExpression: ERROR expected a number, type = " << opCode.type;
|
||||
assert(false);
|
||||
return OpCode(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void AnimExpression::dumpOpCodes() const {
|
||||
QString tmp;
|
||||
for (auto& op : _opCodes) {
|
||||
switch (op.type) {
|
||||
case OpCode::Identifier: tmp += QString(" %1").arg(op.strVal); break;
|
||||
case OpCode::Bool: tmp += QString(" %1").arg(op.intVal ? "true" : "false"); break;
|
||||
case OpCode::Int: tmp += QString(" %1").arg(op.intVal); break;
|
||||
case OpCode::Float: tmp += QString(" %1").arg(op.floatVal); break;
|
||||
case OpCode::And: tmp += " &&"; break;
|
||||
case OpCode::Or: tmp += " ||"; break;
|
||||
case OpCode::GreaterThan: tmp += " >"; break;
|
||||
case OpCode::GreaterThanEqual: tmp += " >="; break;
|
||||
case OpCode::LessThan: tmp += " <"; break;
|
||||
case OpCode::LessThanEqual: tmp += " <="; break;
|
||||
case OpCode::Equal: tmp += " =="; break;
|
||||
case OpCode::NotEqual: tmp += " !="; break;
|
||||
case OpCode::Not: tmp += " !"; break;
|
||||
case OpCode::Subtract: tmp += " -"; break;
|
||||
case OpCode::Add: tmp += " +"; break;
|
||||
case OpCode::Multiply: tmp += " *"; break;
|
||||
case OpCode::Divide: tmp += " /"; break;
|
||||
case OpCode::Modulus: tmp += " %"; break;
|
||||
case OpCode::UnaryMinus: tmp += " unary-"; break;
|
||||
default: tmp += " ???"; break;
|
||||
}
|
||||
}
|
||||
qCDebug(animation).nospace().noquote() << "opCodes =" << tmp;
|
||||
qCDebug(animation).resetFormat();
|
||||
}
|
||||
#endif
|
158
libraries/animation/src/AnimExpression.h
Normal file
158
libraries/animation/src/AnimExpression.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// AnimExpression.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 11/1/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AnimExpression
|
||||
#define hifi_AnimExpression
|
||||
|
||||
#include <QString>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include "AnimVariant.h"
|
||||
|
||||
class AnimExpression {
|
||||
public:
|
||||
friend class AnimTests;
|
||||
AnimExpression(const QString& str);
|
||||
protected:
|
||||
struct Token {
|
||||
enum Type {
|
||||
End = 0,
|
||||
Identifier,
|
||||
Bool,
|
||||
Int,
|
||||
Float,
|
||||
And,
|
||||
Or,
|
||||
GreaterThan,
|
||||
GreaterThanEqual,
|
||||
LessThan,
|
||||
LessThanEqual,
|
||||
Equal,
|
||||
NotEqual,
|
||||
LeftParen,
|
||||
RightParen,
|
||||
Not,
|
||||
Minus,
|
||||
Plus,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulus,
|
||||
Comma,
|
||||
Error
|
||||
};
|
||||
Token(Type type) : type {type} {}
|
||||
Token(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {}
|
||||
explicit Token(int val) : type {Type::Int}, intVal {val} {}
|
||||
explicit Token(bool val) : type {Type::Bool}, intVal {val} {}
|
||||
explicit Token(float val) : type {Type::Float}, floatVal {val} {}
|
||||
Type type {End};
|
||||
QString strVal;
|
||||
int intVal {0};
|
||||
float floatVal {0.0f};
|
||||
};
|
||||
|
||||
struct OpCode {
|
||||
enum Type {
|
||||
Identifier,
|
||||
Bool,
|
||||
Int,
|
||||
Float,
|
||||
And,
|
||||
Or,
|
||||
GreaterThan,
|
||||
GreaterThanEqual,
|
||||
LessThan,
|
||||
LessThanEqual,
|
||||
Equal,
|
||||
NotEqual,
|
||||
Not,
|
||||
Subtract,
|
||||
Add,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulus,
|
||||
UnaryMinus
|
||||
};
|
||||
OpCode(Type type) : type {type} {}
|
||||
explicit OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {}
|
||||
explicit OpCode(const QString& str) : type {Type::Identifier}, strVal {str} {}
|
||||
explicit OpCode(int val) : type {Type::Int}, intVal {val} {}
|
||||
explicit OpCode(bool val) : type {Type::Bool}, intVal {(int)val} {}
|
||||
explicit OpCode(float val) : type {Type::Float}, floatVal {val} {}
|
||||
|
||||
bool coerceBool(const AnimVariantMap& map) const {
|
||||
if (type == Int || type == Bool) {
|
||||
return intVal != 0;
|
||||
} else if (type == Identifier) {
|
||||
return map.lookup(strVal, false);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Type type {Int};
|
||||
QString strVal;
|
||||
int intVal {0};
|
||||
float floatVal {0.0f};
|
||||
};
|
||||
|
||||
void unconsumeToken(const Token& token);
|
||||
Token consumeToken(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeNumber(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeAnd(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeOr(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeGreaterThan(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeLessThan(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeNot(const QString& str, QString::const_iterator& iter) const;
|
||||
|
||||
bool parseExpr(const QString& str, QString::const_iterator& iter);
|
||||
bool parseExprPrime(const QString& str, QString::const_iterator& iter);
|
||||
bool parseTerm(const QString& str, QString::const_iterator& iter);
|
||||
bool parseTermPrime(const QString& str, QString::const_iterator& iter);
|
||||
bool parseUnary(const QString& str, QString::const_iterator& iter);
|
||||
bool parseFactor(const QString& str, QString::const_iterator& iter);
|
||||
|
||||
OpCode evaluate(const AnimVariantMap& map) const;
|
||||
void evalAnd(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalOr(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalGreaterThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalGreaterThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalLessThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalLessThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalNotEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalNot(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalSubtract(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void add(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void add(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void evalMultiply(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void mul(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void mul(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void evalDivide(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalModulus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
|
||||
OpCode coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const;
|
||||
|
||||
QString _expression;
|
||||
mutable std::stack<Token> _tokenStack; // TODO: remove, only needed during parsing
|
||||
std::vector<OpCode> _opCodes;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void dumpOpCodes() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
#include <RegisteredMetaTypes.h>
|
||||
#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap
|
||||
|
||||
const AnimVariant AnimVariant::False = AnimVariant();
|
||||
|
||||
QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const {
|
||||
if (QThread::currentThread() != engine->thread()) {
|
||||
qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread();
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
NumTypes
|
||||
};
|
||||
|
||||
static const AnimVariant False;
|
||||
|
||||
AnimVariant() : _type(Type::Bool) { memset(&_val, 0, sizeof(_val)); }
|
||||
AnimVariant(bool value) : _type(Type::Bool) { _val.boolVal = value; }
|
||||
AnimVariant(int value) : _type(Type::Int) { _val.intVal = value; }
|
||||
|
@ -57,13 +59,50 @@ public:
|
|||
void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast<glm::quat*>(&_val) = value; }
|
||||
void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
|
||||
|
||||
bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
|
||||
int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; }
|
||||
float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; }
|
||||
|
||||
const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
|
||||
const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
|
||||
const QString& getString() const { assert(_type == Type::String); return _stringVal; }
|
||||
bool getBool() const {
|
||||
if (_type == Type::Bool) {
|
||||
return _val.boolVal;
|
||||
} else if (_type == Type::Int) {
|
||||
return _val.intVal != 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int getInt() const {
|
||||
if (_type == Type::Int) {
|
||||
return _val.intVal;
|
||||
} else if (_type == Type::Float) {
|
||||
return (int)_val.floats[0];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
float getFloat() const {
|
||||
if (_type == Type::Float) {
|
||||
return _val.floats[0];
|
||||
} else if (_type == Type::Int) {
|
||||
return (float)_val.intVal;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
const glm::vec3& getVec3() const {
|
||||
if (_type == Type::Vec3) {
|
||||
return *reinterpret_cast<const glm::vec3*>(&_val);
|
||||
} else {
|
||||
return Vectors::ZERO;
|
||||
}
|
||||
}
|
||||
const glm::quat& getQuat() const {
|
||||
if (_type == Type::Quat) {
|
||||
return *reinterpret_cast<const glm::quat*>(&_val);
|
||||
} else {
|
||||
return Quaternions::IDENTITY;
|
||||
}
|
||||
}
|
||||
const QString& getString() const {
|
||||
return _stringVal;
|
||||
}
|
||||
|
||||
protected:
|
||||
Type _type;
|
||||
|
@ -71,7 +110,7 @@ protected:
|
|||
union {
|
||||
bool boolVal;
|
||||
int intVal;
|
||||
float floats[16];
|
||||
float floats[4];
|
||||
} _val;
|
||||
};
|
||||
|
||||
|
@ -172,6 +211,15 @@ public:
|
|||
void clearMap() { _map.clear(); }
|
||||
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
|
||||
|
||||
const AnimVariant& get(const QString& key) const {
|
||||
auto iter = _map.find(key);
|
||||
if (iter != _map.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
return AnimVariant::False;
|
||||
}
|
||||
}
|
||||
|
||||
// Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties.
|
||||
QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const;
|
||||
// Side-effect us with the value of object's own properties. (No inherited properties.)
|
||||
|
|
|
@ -289,8 +289,10 @@ void Rig::clearJointState(int index) {
|
|||
|
||||
void Rig::clearJointStates() {
|
||||
_internalPoseSet._overrideFlags.clear();
|
||||
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints());
|
||||
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||
if (_animSkeleton) {
|
||||
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints());
|
||||
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::clearJointAnimationPriority(int index) {
|
||||
|
|
|
@ -88,15 +88,73 @@ namespace controller {
|
|||
|
||||
// No correlation to SDL
|
||||
enum StandardPoseChannel {
|
||||
LEFT_HAND = 0,
|
||||
RIGHT_HAND,
|
||||
HIPS = 0,
|
||||
RIGHT_UP_LEG,
|
||||
RIGHT_LEG,
|
||||
RIGHT_FOOT,
|
||||
LEFT_UP_LEG,
|
||||
LEFT_LEG,
|
||||
LEFT_FOOT,
|
||||
SPINE,
|
||||
SPINE1,
|
||||
SPINE2,
|
||||
SPINE3,
|
||||
NECK,
|
||||
HEAD,
|
||||
RIGHT_SHOULDER,
|
||||
RIGHT_ARM,
|
||||
RIGHT_FORE_ARM,
|
||||
RIGHT_HAND,
|
||||
RIGHT_HAND_THUMB1,
|
||||
RIGHT_HAND_THUMB2,
|
||||
RIGHT_HAND_THUMB3,
|
||||
RIGHT_HAND_THUMB4,
|
||||
RIGHT_HAND_INDEX1,
|
||||
RIGHT_HAND_INDEX2,
|
||||
RIGHT_HAND_INDEX3,
|
||||
RIGHT_HAND_INDEX4,
|
||||
RIGHT_HAND_MIDDLE1,
|
||||
RIGHT_HAND_MIDDLE2,
|
||||
RIGHT_HAND_MIDDLE3,
|
||||
RIGHT_HAND_MIDDLE4,
|
||||
RIGHT_HAND_RING1,
|
||||
RIGHT_HAND_RING2,
|
||||
RIGHT_HAND_RING3,
|
||||
RIGHT_HAND_RING4,
|
||||
RIGHT_HAND_PINKY1,
|
||||
RIGHT_HAND_PINKY2,
|
||||
RIGHT_HAND_PINKY3,
|
||||
RIGHT_HAND_PINKY4,
|
||||
LEFT_SHOULDER,
|
||||
LEFT_ARM,
|
||||
LEFT_FORE_ARM,
|
||||
LEFT_HAND,
|
||||
LEFT_HAND_THUMB1,
|
||||
LEFT_HAND_THUMB2,
|
||||
LEFT_HAND_THUMB3,
|
||||
LEFT_HAND_THUMB4,
|
||||
LEFT_HAND_INDEX1,
|
||||
LEFT_HAND_INDEX2,
|
||||
LEFT_HAND_INDEX3,
|
||||
LEFT_HAND_INDEX4,
|
||||
LEFT_HAND_MIDDLE1,
|
||||
LEFT_HAND_MIDDLE2,
|
||||
LEFT_HAND_MIDDLE3,
|
||||
LEFT_HAND_MIDDLE4,
|
||||
LEFT_HAND_RING1,
|
||||
LEFT_HAND_RING2,
|
||||
LEFT_HAND_RING3,
|
||||
LEFT_HAND_RING4,
|
||||
LEFT_HAND_PINKY1,
|
||||
LEFT_HAND_PINKY2,
|
||||
LEFT_HAND_PINKY3,
|
||||
LEFT_HAND_PINKY4,
|
||||
NUM_STANDARD_POSES
|
||||
};
|
||||
|
||||
enum StandardCounts {
|
||||
TRIGGERS = 2,
|
||||
ANALOG_STICKS = 2,
|
||||
POSES = 2, // FIXME 3? if we want to expose the head?
|
||||
POSES = NUM_STANDARD_POSES
|
||||
};
|
||||
}
|
||||
|
|
|
@ -96,7 +96,12 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
// Render
|
||||
gpu::TexturePointer skymap = skybox.getCubemap();
|
||||
// FIXME: skymap->isDefined may not be threadsafe
|
||||
assert(skymap && skymap->isDefined());
|
||||
|
||||
glm::mat4 projMat;
|
||||
viewFrustum.evalProjectionMatrix(projMat);
|
||||
|
||||
|
@ -106,11 +111,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
|
||||
gpu::TexturePointer skymap;
|
||||
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
||||
skymap = skybox.getCubemap();
|
||||
}
|
||||
|
||||
batch.setPipeline(thePipeline);
|
||||
batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer);
|
||||
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap);
|
||||
|
@ -118,6 +118,5 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -234,6 +234,7 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes)
|
|||
void NodeList::sendDomainServerCheckIn() {
|
||||
if (_isShuttingDown) {
|
||||
qCDebug(networking) << "Refusing to send a domain-server check in while shutting down.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_publicSockAddr.isNull()) {
|
||||
|
|
|
@ -48,6 +48,10 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum,
|
|||
}
|
||||
|
||||
if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) {
|
||||
gpu::TexturePointer skymap = skybox.getCubemap();
|
||||
// FIXME: skymap->isDefined may not be threadsafe
|
||||
assert(skymap && skymap->isDefined());
|
||||
|
||||
glm::mat4 projMat;
|
||||
viewFrustum.evalProjectionMatrix(projMat);
|
||||
|
||||
|
@ -56,10 +60,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum,
|
|||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
|
||||
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
||||
batch.setResourceTexture(0, skybox.getCubemap());
|
||||
}
|
||||
batch.setResourceTexture(0, skybox.getCubemap());
|
||||
|
||||
skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
static const vec3& RIGHT;
|
||||
static const vec3& UP;
|
||||
static const vec3& FRONT;
|
||||
static const vec3 ZERO4;
|
||||
};
|
||||
|
||||
// These pack/unpack functions are designed to start specific known types in as efficient a manner
|
||||
|
|
13
plugins/hifiNeuron/CMakeLists.txt
Normal file
13
plugins/hifiNeuron/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Created by Anthony Thibault on 2015/12/18
|
||||
# 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
|
||||
#
|
||||
|
||||
set(TARGET_NAME hifiNeuron)
|
||||
setup_hifi_plugin(Script Qml Widgets)
|
||||
link_hifi_libraries(shared controllers plugins input-plugins)
|
||||
target_neuron()
|
||||
|
557
plugins/hifiNeuron/src/NeuronPlugin.cpp
Normal file
557
plugins/hifiNeuron/src/NeuronPlugin.cpp
Normal file
|
@ -0,0 +1,557 @@
|
|||
//
|
||||
// NeuronPlugin.cpp
|
||||
// input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Anthony Thibault on 12/18/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
|
||||
//
|
||||
|
||||
#include "NeuronPlugin.h"
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
#include <QLoggingCategory>
|
||||
#include <PathUtils.h>
|
||||
#include <DebugDraw.h>
|
||||
#include <cassert>
|
||||
#include <NumericalConstants.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
|
||||
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
|
||||
|
||||
#define __OS_XUN__ 1
|
||||
#define BOOL int
|
||||
|
||||
#ifdef HAVE_NEURON
|
||||
#include <NeuronDataReader.h>
|
||||
#endif
|
||||
|
||||
const QString NeuronPlugin::NAME = "Neuron";
|
||||
const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron";
|
||||
|
||||
// indices of joints of the Neuron standard skeleton.
|
||||
// This is 'almost' the same as the High Fidelity standard skeleton.
|
||||
// It is missing a thumb joint.
|
||||
enum NeuronJointIndex {
|
||||
Hips = 0,
|
||||
RightUpLeg,
|
||||
RightLeg,
|
||||
RightFoot,
|
||||
LeftUpLeg,
|
||||
LeftLeg,
|
||||
LeftFoot,
|
||||
Spine,
|
||||
Spine1,
|
||||
Spine2,
|
||||
Spine3,
|
||||
Neck,
|
||||
Head,
|
||||
RightShoulder,
|
||||
RightArm,
|
||||
RightForeArm,
|
||||
RightHand,
|
||||
RightHandThumb1,
|
||||
RightHandThumb2,
|
||||
RightHandThumb3,
|
||||
RightInHandIndex,
|
||||
RightHandIndex1,
|
||||
RightHandIndex2,
|
||||
RightHandIndex3,
|
||||
RightInHandMiddle,
|
||||
RightHandMiddle1,
|
||||
RightHandMiddle2,
|
||||
RightHandMiddle3,
|
||||
RightInHandRing,
|
||||
RightHandRing1,
|
||||
RightHandRing2,
|
||||
RightHandRing3,
|
||||
RightInHandPinky,
|
||||
RightHandPinky1,
|
||||
RightHandPinky2,
|
||||
RightHandPinky3,
|
||||
LeftShoulder,
|
||||
LeftArm,
|
||||
LeftForeArm,
|
||||
LeftHand,
|
||||
LeftHandThumb1,
|
||||
LeftHandThumb2,
|
||||
LeftHandThumb3,
|
||||
LeftInHandIndex,
|
||||
LeftHandIndex1,
|
||||
LeftHandIndex2,
|
||||
LeftHandIndex3,
|
||||
LeftInHandMiddle,
|
||||
LeftHandMiddle1,
|
||||
LeftHandMiddle2,
|
||||
LeftHandMiddle3,
|
||||
LeftInHandRing,
|
||||
LeftHandRing1,
|
||||
LeftHandRing2,
|
||||
LeftHandRing3,
|
||||
LeftInHandPinky,
|
||||
LeftHandPinky1,
|
||||
LeftHandPinky2,
|
||||
LeftHandPinky3,
|
||||
Size
|
||||
};
|
||||
|
||||
// Almost a direct mapping except for LEFT_HAND_THUMB1 and RIGHT_HAND_THUMB1,
|
||||
// which are not present in the Neuron standard skeleton.
|
||||
static controller::StandardPoseChannel neuronJointIndexToPoseIndexMap[NeuronJointIndex::Size] = {
|
||||
controller::HIPS,
|
||||
controller::RIGHT_UP_LEG,
|
||||
controller::RIGHT_LEG,
|
||||
controller::RIGHT_FOOT,
|
||||
controller::LEFT_UP_LEG,
|
||||
controller::LEFT_LEG,
|
||||
controller::LEFT_FOOT,
|
||||
controller::SPINE,
|
||||
controller::SPINE1,
|
||||
controller::SPINE2,
|
||||
controller::SPINE3,
|
||||
controller::NECK,
|
||||
controller::HEAD,
|
||||
controller::RIGHT_SHOULDER,
|
||||
controller::RIGHT_ARM,
|
||||
controller::RIGHT_FORE_ARM,
|
||||
controller::RIGHT_HAND,
|
||||
controller::RIGHT_HAND_THUMB2,
|
||||
controller::RIGHT_HAND_THUMB3,
|
||||
controller::RIGHT_HAND_THUMB4,
|
||||
controller::RIGHT_HAND_INDEX1,
|
||||
controller::RIGHT_HAND_INDEX2,
|
||||
controller::RIGHT_HAND_INDEX3,
|
||||
controller::RIGHT_HAND_INDEX4,
|
||||
controller::RIGHT_HAND_MIDDLE1,
|
||||
controller::RIGHT_HAND_MIDDLE2,
|
||||
controller::RIGHT_HAND_MIDDLE3,
|
||||
controller::RIGHT_HAND_MIDDLE4,
|
||||
controller::RIGHT_HAND_RING1,
|
||||
controller::RIGHT_HAND_RING2,
|
||||
controller::RIGHT_HAND_RING3,
|
||||
controller::RIGHT_HAND_RING4,
|
||||
controller::RIGHT_HAND_PINKY1,
|
||||
controller::RIGHT_HAND_PINKY2,
|
||||
controller::RIGHT_HAND_PINKY3,
|
||||
controller::RIGHT_HAND_PINKY4,
|
||||
controller::LEFT_SHOULDER,
|
||||
controller::LEFT_ARM,
|
||||
controller::LEFT_FORE_ARM,
|
||||
controller::LEFT_HAND,
|
||||
controller::LEFT_HAND_THUMB2,
|
||||
controller::LEFT_HAND_THUMB3,
|
||||
controller::LEFT_HAND_THUMB4,
|
||||
controller::LEFT_HAND_INDEX1,
|
||||
controller::LEFT_HAND_INDEX2,
|
||||
controller::LEFT_HAND_INDEX3,
|
||||
controller::LEFT_HAND_INDEX4,
|
||||
controller::LEFT_HAND_MIDDLE1,
|
||||
controller::LEFT_HAND_MIDDLE2,
|
||||
controller::LEFT_HAND_MIDDLE3,
|
||||
controller::LEFT_HAND_MIDDLE4,
|
||||
controller::LEFT_HAND_RING1,
|
||||
controller::LEFT_HAND_RING2,
|
||||
controller::LEFT_HAND_RING3,
|
||||
controller::LEFT_HAND_RING4,
|
||||
controller::LEFT_HAND_PINKY1,
|
||||
controller::LEFT_HAND_PINKY2,
|
||||
controller::LEFT_HAND_PINKY3,
|
||||
controller::LEFT_HAND_PINKY4
|
||||
};
|
||||
|
||||
// in rig frame
|
||||
static glm::vec3 rightHandThumb1DefaultAbsTranslation(-2.155500650405884, -0.7610001564025879, 2.685631036758423);
|
||||
static glm::vec3 leftHandThumb1DefaultAbsTranslation(2.1555817127227783, -0.7603635787963867, 2.6856393814086914);
|
||||
|
||||
// default translations (cm)
|
||||
static glm::vec3 neuronJointTranslations[NeuronJointIndex::Size] = {
|
||||
{131.901, 95.6602, -27.9815},
|
||||
{-9.55907, -1.58772, 0.0760284},
|
||||
{0.0144232, -41.4683, -0.105322},
|
||||
{1.59348, -41.5875, -0.557237},
|
||||
{9.72077, -1.68926, -0.280643},
|
||||
{0.0886684, -43.1586, -0.0111596},
|
||||
{-2.98473, -44.0517, 0.0694456},
|
||||
{0.110967, 16.3959, 0.140463},
|
||||
{0.0500451, 10.0238, 0.0731921},
|
||||
{0.061568, 10.4352, 0.0583075},
|
||||
{0.0500606, 10.0217, 0.0711083},
|
||||
{0.0317731, 10.7176, 0.0779325},
|
||||
{-0.0204253, 9.71067, 0.131734},
|
||||
{-3.24245, 7.13584, 0.185638},
|
||||
{-13.0885, -0.0877601, 0.176065},
|
||||
{-27.2674, 0.0688724, 0.0272146},
|
||||
{-26.7673, 0.0301916, 0.0102847},
|
||||
{-2.56017, 0.195537, 3.20968},
|
||||
{-3.78796, 0, 0},
|
||||
{-2.63141, 0, 0},
|
||||
{-3.31579, 0.522947, 2.03495},
|
||||
{-5.36589, -0.0939789, 1.02771},
|
||||
{-3.72278, 0, 0},
|
||||
{-2.11074, 0, 0},
|
||||
{-3.47874, 0.532042, 0.778358},
|
||||
{-5.32194, -0.0864, 0.322863},
|
||||
{-4.06232, 0, 0},
|
||||
{-2.54653, 0, 0},
|
||||
{-3.46131, 0.553263, -0.132632},
|
||||
{-4.76716, -0.0227368, -0.492632},
|
||||
{-3.54073, 0, 0},
|
||||
{-2.45634, 0, 0},
|
||||
{-3.25137, 0.482779, -1.23613},
|
||||
{-4.25937, -0.0227368, -1.12168},
|
||||
{-2.83528, 0, 0},
|
||||
{-1.79166, 0, 0},
|
||||
{3.25624, 7.13148, -0.131575},
|
||||
{13.149, -0.052598, -0.125076},
|
||||
{27.2903, 0.00282644, -0.0181535},
|
||||
{26.6602, 0.000969969, -0.0487599},
|
||||
{2.56017, 0.195537, 3.20968},
|
||||
{3.78796, 0, 0},
|
||||
{2.63141, 0, 0},
|
||||
{3.31579, 0.522947, 2.03495},
|
||||
{5.36589, -0.0939789, 1.02771},
|
||||
{3.72278, 0, 0},
|
||||
{2.11074, 0, 0},
|
||||
{3.47874, 0.532042, 0.778358},
|
||||
{5.32194, -0.0864, 0.322863},
|
||||
{4.06232, 0, 0},
|
||||
{2.54653, 0, 0},
|
||||
{3.46131, 0.553263, -0.132632},
|
||||
{4.76716, -0.0227368, -0.492632},
|
||||
{3.54073, 0, 0},
|
||||
{2.45634, 0, 0},
|
||||
{3.25137, 0.482779, -1.23613},
|
||||
{4.25937, -0.0227368, -1.12168},
|
||||
{2.83528, 0, 0},
|
||||
{1.79166, 0, 0}
|
||||
};
|
||||
|
||||
static controller::StandardPoseChannel neuronJointIndexToPoseIndex(NeuronJointIndex i) {
|
||||
assert(i >= 0 && i < NeuronJointIndex::Size);
|
||||
if (i >= 0 && i < NeuronJointIndex::Size) {
|
||||
return neuronJointIndexToPoseIndexMap[i];
|
||||
} else {
|
||||
return (controller::StandardPoseChannel)0; // not sure what to do here, but don't crash!
|
||||
}
|
||||
}
|
||||
|
||||
static const char* controllerJointName(controller::StandardPoseChannel i) {
|
||||
switch (i) {
|
||||
case controller::HIPS: return "Hips";
|
||||
case controller::RIGHT_UP_LEG: return "RightUpLeg";
|
||||
case controller::RIGHT_LEG: return "RightLeg";
|
||||
case controller::RIGHT_FOOT: return "RightFoot";
|
||||
case controller::LEFT_UP_LEG: return "LeftUpLeg";
|
||||
case controller::LEFT_LEG: return "LeftLeg";
|
||||
case controller::LEFT_FOOT: return "LeftFoot";
|
||||
case controller::SPINE: return "Spine";
|
||||
case controller::SPINE1: return "Spine1";
|
||||
case controller::SPINE2: return "Spine2";
|
||||
case controller::SPINE3: return "Spine3";
|
||||
case controller::NECK: return "Neck";
|
||||
case controller::HEAD: return "Head";
|
||||
case controller::RIGHT_SHOULDER: return "RightShoulder";
|
||||
case controller::RIGHT_ARM: return "RightArm";
|
||||
case controller::RIGHT_FORE_ARM: return "RightForeArm";
|
||||
case controller::RIGHT_HAND: return "RightHand";
|
||||
case controller::RIGHT_HAND_THUMB1: return "RightHandThumb1";
|
||||
case controller::RIGHT_HAND_THUMB2: return "RightHandThumb2";
|
||||
case controller::RIGHT_HAND_THUMB3: return "RightHandThumb3";
|
||||
case controller::RIGHT_HAND_THUMB4: return "RightHandThumb4";
|
||||
case controller::RIGHT_HAND_INDEX1: return "RightHandIndex1";
|
||||
case controller::RIGHT_HAND_INDEX2: return "RightHandIndex2";
|
||||
case controller::RIGHT_HAND_INDEX3: return "RightHandIndex3";
|
||||
case controller::RIGHT_HAND_INDEX4: return "RightHandIndex4";
|
||||
case controller::RIGHT_HAND_MIDDLE1: return "RightHandMiddle1";
|
||||
case controller::RIGHT_HAND_MIDDLE2: return "RightHandMiddle2";
|
||||
case controller::RIGHT_HAND_MIDDLE3: return "RightHandMiddle3";
|
||||
case controller::RIGHT_HAND_MIDDLE4: return "RightHandMiddle4";
|
||||
case controller::RIGHT_HAND_RING1: return "RightHandRing1";
|
||||
case controller::RIGHT_HAND_RING2: return "RightHandRing2";
|
||||
case controller::RIGHT_HAND_RING3: return "RightHandRing3";
|
||||
case controller::RIGHT_HAND_RING4: return "RightHandRing4";
|
||||
case controller::RIGHT_HAND_PINKY1: return "RightHandPinky1";
|
||||
case controller::RIGHT_HAND_PINKY2: return "RightHandPinky2";
|
||||
case controller::RIGHT_HAND_PINKY3: return "RightHandPinky3";
|
||||
case controller::RIGHT_HAND_PINKY4: return "RightHandPinky4";
|
||||
case controller::LEFT_SHOULDER: return "LeftShoulder";
|
||||
case controller::LEFT_ARM: return "LeftArm";
|
||||
case controller::LEFT_FORE_ARM: return "LeftForeArm";
|
||||
case controller::LEFT_HAND: return "LeftHand";
|
||||
case controller::LEFT_HAND_THUMB1: return "LeftHandThumb1";
|
||||
case controller::LEFT_HAND_THUMB2: return "LeftHandThumb2";
|
||||
case controller::LEFT_HAND_THUMB3: return "LeftHandThumb3";
|
||||
case controller::LEFT_HAND_THUMB4: return "LeftHandThumb4";
|
||||
case controller::LEFT_HAND_INDEX1: return "LeftHandIndex1";
|
||||
case controller::LEFT_HAND_INDEX2: return "LeftHandIndex2";
|
||||
case controller::LEFT_HAND_INDEX3: return "LeftHandIndex3";
|
||||
case controller::LEFT_HAND_INDEX4: return "LeftHandIndex4";
|
||||
case controller::LEFT_HAND_MIDDLE1: return "LeftHandMiddle1";
|
||||
case controller::LEFT_HAND_MIDDLE2: return "LeftHandMiddle2";
|
||||
case controller::LEFT_HAND_MIDDLE3: return "LeftHandMiddle3";
|
||||
case controller::LEFT_HAND_MIDDLE4: return "LeftHandMiddle4";
|
||||
case controller::LEFT_HAND_RING1: return "LeftHandRing1";
|
||||
case controller::LEFT_HAND_RING2: return "LeftHandRing2";
|
||||
case controller::LEFT_HAND_RING3: return "LeftHandRing3";
|
||||
case controller::LEFT_HAND_RING4: return "LeftHandRing4";
|
||||
case controller::LEFT_HAND_PINKY1: return "LeftHandPinky1";
|
||||
case controller::LEFT_HAND_PINKY2: return "LeftHandPinky2";
|
||||
case controller::LEFT_HAND_PINKY3: return "LeftHandPinky3";
|
||||
case controller::LEFT_HAND_PINKY4: return "LeftHandPinky4";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
// convert between YXZ neuron euler angles in degrees to quaternion
|
||||
// this is the default setting in the Axis Neuron server.
|
||||
static quat eulerToQuat(vec3 euler) {
|
||||
// euler.x and euler.y are swaped, WTF.
|
||||
glm::vec3 e = glm::vec3(euler.y, euler.x, euler.z) * RADIANS_PER_DEGREE;
|
||||
return (glm::angleAxis(e.y, Vectors::UNIT_Y) *
|
||||
glm::angleAxis(e.x, Vectors::UNIT_X) *
|
||||
glm::angleAxis(e.z, Vectors::UNIT_Z));
|
||||
}
|
||||
|
||||
#ifdef HAVE_NEURON
|
||||
|
||||
//
|
||||
// neuronDataReader SDK callback functions
|
||||
//
|
||||
|
||||
// NOTE: must be thread-safe
|
||||
void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx* header, float* data) {
|
||||
|
||||
auto neuronPlugin = reinterpret_cast<NeuronPlugin*>(context);
|
||||
|
||||
// version 1.0
|
||||
if (header->DataVersion.Major == 1 && header->DataVersion.Minor == 0) {
|
||||
|
||||
// skip reference joint if present
|
||||
if (header->WithReference && header->WithDisp) {
|
||||
data += 6;
|
||||
} else if (header->WithReference && !header->WithDisp) {
|
||||
data += 3;
|
||||
}
|
||||
|
||||
if (header->WithDisp) {
|
||||
// enter mutex
|
||||
std::lock_guard<std::mutex> guard(neuronPlugin->_jointsMutex);
|
||||
|
||||
//
|
||||
// Data is 6 floats per joint: 3 position values, 3 rotation euler angles (degrees)
|
||||
//
|
||||
|
||||
// resize vector if necessary
|
||||
const size_t NUM_FLOATS_PER_JOINT = 6;
|
||||
const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT;
|
||||
if (neuronPlugin->_joints.size() != NUM_JOINTS) {
|
||||
neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
|
||||
}
|
||||
|
||||
assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float)));
|
||||
|
||||
// copy the data
|
||||
memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS);
|
||||
|
||||
} else {
|
||||
qCWarning(inputplugins) << "NeuronPlugin: unsuported binary format, please enable displacements";
|
||||
|
||||
// enter mutex
|
||||
std::lock_guard<std::mutex> guard(neuronPlugin->_jointsMutex);
|
||||
|
||||
if (neuronPlugin->_joints.size() != NeuronJointIndex::Size) {
|
||||
neuronPlugin->_joints.resize(NeuronJointIndex::Size, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
|
||||
}
|
||||
|
||||
for (int i = 0; i < NeuronJointIndex::Size; i++) {
|
||||
neuronPlugin->_joints[i].euler = glm::vec3();
|
||||
neuronPlugin->_joints[i].pos = neuronJointTranslations[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
static bool ONCE = false;
|
||||
if (!ONCE) {
|
||||
qCCritical(inputplugins) << "NeuronPlugin: bad frame version number, expected 1.0";
|
||||
ONCE = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// I can't even get the SDK to send me a callback.
|
||||
// BRCommandFetchAvatarDataFromServer & BRRegisterAutoSyncParmeter [sic] don't seem to work.
|
||||
// So this is totally untested.
|
||||
// NOTE: must be thread-safe
|
||||
static void CommandDataReceivedCallback(void* context, SOCKET_REF sender, CommandPack* pack, void* data) {
|
||||
|
||||
DATA_VER version;
|
||||
version._VersionMask = pack->DataVersion;
|
||||
if (version.Major == 1 && version.Minor == 0) {
|
||||
const char* str = "Unknown";
|
||||
switch (pack->CommandId) {
|
||||
case Cmd_BoneSize: // Id can be used to request bone size from server or register avatar name command.
|
||||
str = "BoneSize";
|
||||
break;
|
||||
case Cmd_AvatarName: // Id can be used to request avatar name from server or register avatar name command.
|
||||
str = "AvatarName";
|
||||
break;
|
||||
case Cmd_FaceDirection: // Id used to request face direction from server
|
||||
str = "FaceDirection";
|
||||
break;
|
||||
case Cmd_DataFrequency: // Id can be used to request data frequency from server or register data frequency command.
|
||||
str = "DataFrequency";
|
||||
break;
|
||||
case Cmd_BvhInheritanceTxt: // Id can be used to request bvh header txt from server or register bvh header txt command.
|
||||
str = "BvhInheritanceTxt";
|
||||
break;
|
||||
case Cmd_AvatarCount: // Id can be used to request avatar count from server or register avatar count command.
|
||||
str = "AvatarCount";
|
||||
break;
|
||||
case Cmd_CombinationMode: // Id can be used to request combination mode from server or register combination mode command.
|
||||
str = "CombinationMode";
|
||||
break;
|
||||
case Cmd_RegisterEvent: // Id can be used to register event.
|
||||
str = "RegisterEvent";
|
||||
break;
|
||||
case Cmd_UnRegisterEvent: // Id can be used to unregister event.
|
||||
str = "UnRegisterEvent";
|
||||
break;
|
||||
}
|
||||
qCDebug(inputplugins) << "NeuronPlugin: command data received CommandID = " << str;
|
||||
} else {
|
||||
static bool ONCE = false;
|
||||
if (!ONCE) {
|
||||
qCCritical(inputplugins) << "NeuronPlugin: bad command version number, expected 1.0";
|
||||
ONCE = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: must be thread-safe
|
||||
static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, SocketStatus status, char* message) {
|
||||
// just dump to log, later we might want to pop up a connection lost dialog or attempt to reconnect.
|
||||
qCDebug(inputplugins) << "NeuronPlugin: socket status = " << message;
|
||||
}
|
||||
|
||||
#endif // #ifdef HAVE_NEURON
|
||||
|
||||
//
|
||||
// NeuronPlugin
|
||||
//
|
||||
|
||||
bool NeuronPlugin::isSupported() const {
|
||||
#ifdef HAVE_NEURON
|
||||
// Because it's a client/server network architecture, we can't tell
|
||||
// if the neuron is actually connected until we connect to the server.
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void NeuronPlugin::activate() {
|
||||
#ifdef HAVE_NEURON
|
||||
InputPlugin::activate();
|
||||
|
||||
// register with userInputMapper
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
userInputMapper->registerDevice(_inputDevice);
|
||||
|
||||
// register c-style callbacks
|
||||
BRRegisterFrameDataCallback((void*)this, FrameDataReceivedCallback);
|
||||
BRRegisterCommandDataCallback((void*)this, CommandDataReceivedCallback);
|
||||
BRRegisterSocketStatusCallback((void*)this, SocketStatusChangedCallback);
|
||||
|
||||
// TODO: Pull these from prefs dialog?
|
||||
// localhost is fine for now.
|
||||
_serverAddress = "localhost";
|
||||
_serverPort = 7001; // default port for TCP Axis Neuron server.
|
||||
|
||||
_socketRef = BRConnectTo((char*)_serverAddress.c_str(), _serverPort);
|
||||
if (!_socketRef) {
|
||||
// error
|
||||
qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << ", error = " << BRGetLastErrorMessage();
|
||||
} else {
|
||||
qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort;
|
||||
|
||||
BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void NeuronPlugin::deactivate() {
|
||||
#ifdef HAVE_NEURON
|
||||
// unregister from userInputMapper
|
||||
if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) {
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
userInputMapper->removeDevice(_inputDevice->_deviceID);
|
||||
}
|
||||
|
||||
if (_socketRef) {
|
||||
BRUnregisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode);
|
||||
BRCloseSocket(_socketRef);
|
||||
}
|
||||
|
||||
InputPlugin::deactivate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) {
|
||||
std::vector<NeuronJoint> joints;
|
||||
{
|
||||
// lock and copy
|
||||
std::lock_guard<std::mutex> guard(_jointsMutex);
|
||||
joints = _joints;
|
||||
}
|
||||
_inputDevice->update(deltaTime, joints, _prevJoints);
|
||||
_prevJoints = joints;
|
||||
}
|
||||
|
||||
void NeuronPlugin::saveSettings() const {
|
||||
InputPlugin::saveSettings();
|
||||
}
|
||||
|
||||
void NeuronPlugin::loadSettings() {
|
||||
InputPlugin::loadSettings();
|
||||
}
|
||||
|
||||
//
|
||||
// InputDevice
|
||||
//
|
||||
|
||||
controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const {
|
||||
static controller::Input::NamedVector availableInputs;
|
||||
if (availableInputs.size() == 0) {
|
||||
for (int i = 0; i < controller::NUM_STANDARD_POSES; i++) {
|
||||
auto channel = static_cast<controller::StandardPoseChannel>(i);
|
||||
availableInputs.push_back(makePair(channel, controllerJointName(channel)));
|
||||
}
|
||||
};
|
||||
return availableInputs;
|
||||
}
|
||||
|
||||
QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const {
|
||||
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/neuron.json";
|
||||
return MAPPING_JSON;
|
||||
}
|
||||
|
||||
void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints) {
|
||||
for (size_t i = 0; i < joints.size(); i++) {
|
||||
glm::vec3 linearVel, angularVel;
|
||||
glm::vec3 pos = joints[i].pos;
|
||||
glm::quat rot = eulerToQuat(joints[i].euler);
|
||||
if (i < prevJoints.size()) {
|
||||
linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; // m/s
|
||||
// quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation.
|
||||
glm::quat d = glm::log(rot * glm::inverse(eulerToQuat(prevJoints[i].euler)));
|
||||
angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s
|
||||
}
|
||||
int poseIndex = neuronJointIndexToPoseIndex((NeuronJointIndex)i);
|
||||
_poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVel, angularVel);
|
||||
}
|
||||
|
||||
_poseStateMap[controller::RIGHT_HAND_THUMB1] = controller::Pose(rightHandThumb1DefaultAbsTranslation, glm::quat(), glm::vec3(), glm::vec3());
|
||||
_poseStateMap[controller::LEFT_HAND_THUMB1] = controller::Pose(leftHandThumb1DefaultAbsTranslation, glm::quat(), glm::vec3(), glm::vec3());
|
||||
}
|
85
plugins/hifiNeuron/src/NeuronPlugin.h
Normal file
85
plugins/hifiNeuron/src/NeuronPlugin.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// NeuronPlugin.h
|
||||
// input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Anthony Thibault on 12/18/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
|
||||
//
|
||||
|
||||
#ifndef hifi_NeuronPlugin_h
|
||||
#define hifi_NeuronPlugin_h
|
||||
|
||||
#include <controllers/InputDevice.h>
|
||||
#include <controllers/StandardControls.h>
|
||||
#include <plugins/InputPlugin.h>
|
||||
|
||||
struct _BvhDataHeaderEx;
|
||||
void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);
|
||||
|
||||
// Handles interaction with the Neuron SDK
|
||||
class NeuronPlugin : public InputPlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);
|
||||
|
||||
// Plugin functions
|
||||
virtual bool isSupported() const override;
|
||||
virtual bool isJointController() const override { return true; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
const QString& getID() const override { return NEURON_ID_STRING; }
|
||||
|
||||
virtual void activate() override;
|
||||
virtual void deactivate() override;
|
||||
|
||||
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
|
||||
|
||||
virtual void saveSettings() const override;
|
||||
virtual void loadSettings() override;
|
||||
|
||||
protected:
|
||||
|
||||
struct NeuronJoint {
|
||||
glm::vec3 pos;
|
||||
glm::vec3 euler;
|
||||
};
|
||||
|
||||
class InputDevice : public controller::InputDevice {
|
||||
public:
|
||||
friend class NeuronPlugin;
|
||||
|
||||
InputDevice() : controller::InputDevice("Neuron") {}
|
||||
|
||||
// Device functions
|
||||
virtual controller::Input::NamedVector getAvailableInputs() const override;
|
||||
virtual QString getDefaultMappingConfig() const override;
|
||||
virtual void update(float deltaTime, bool jointsCaptured) override {};
|
||||
virtual void focusOutEvent() override {};
|
||||
|
||||
void update(float deltaTime, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints);
|
||||
};
|
||||
|
||||
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };
|
||||
|
||||
static const QString NAME;
|
||||
static const QString NEURON_ID_STRING;
|
||||
|
||||
std::string _serverAddress;
|
||||
int _serverPort;
|
||||
void* _socketRef;
|
||||
|
||||
// used to guard multi-threaded access to _joints
|
||||
std::mutex _jointsMutex;
|
||||
|
||||
// copy of data directly from the NeuronDataReader SDK
|
||||
std::vector<NeuronJoint> _joints;
|
||||
|
||||
// one frame old copy of _joints, used to caluclate angular and linear velocity.
|
||||
std::vector<NeuronJoint> _prevJoints;
|
||||
};
|
||||
|
||||
#endif // hifi_NeuronPlugin_h
|
||||
|
45
plugins/hifiNeuron/src/NeuronProvider.cpp
Normal file
45
plugins/hifiNeuron/src/NeuronProvider.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Created by Anthony Thibault on 2015/12/18
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QtPlugin>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#include <plugins/RuntimePlugin.h>
|
||||
#include <plugins/InputPlugin.h>
|
||||
|
||||
#include "NeuronPlugin.h"
|
||||
|
||||
class NeuronProvider : public QObject, public InputProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json")
|
||||
Q_INTERFACES(InputProvider)
|
||||
|
||||
public:
|
||||
NeuronProvider(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~NeuronProvider() {}
|
||||
|
||||
virtual InputPluginList getInputPlugins() override {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
InputPluginPointer plugin(new NeuronPlugin());
|
||||
if (plugin->isSupported()) {
|
||||
_inputPlugins.push_back(plugin);
|
||||
}
|
||||
});
|
||||
return _inputPlugins;
|
||||
}
|
||||
|
||||
private:
|
||||
InputPluginList _inputPlugins;
|
||||
};
|
||||
|
||||
#include "NeuronProvider.moc"
|
1
plugins/hifiNeuron/src/plugin.json
Normal file
1
plugins/hifiNeuron/src/plugin.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
BIN
stack-manager/assets/icon-beta.ico
Normal file
BIN
stack-manager/assets/icon-beta.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
BIN
stack-manager/assets/icon-beta.png
Normal file
BIN
stack-manager/assets/icon-beta.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -274,7 +274,7 @@ struct ByteData {
|
|||
|
||||
QTextStream & operator << (QTextStream& stream, const ByteData & wrapper) {
|
||||
// Print bytes as hex
|
||||
stream << QByteArray::fromRawData(wrapper.data, wrapper.length).toHex();
|
||||
stream << QByteArray::fromRawData(wrapper.data, (int)wrapper.length).toHex();
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
//
|
||||
|
||||
#include "AnimTests.h"
|
||||
#include "AnimNodeLoader.h"
|
||||
#include "AnimClip.h"
|
||||
#include "AnimBlendLinear.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimVariant.h"
|
||||
#include "AnimUtil.h"
|
||||
#include <AnimNodeLoader.h>
|
||||
#include <AnimClip.h>
|
||||
#include <AnimBlendLinear.h>
|
||||
#include <AnimationLogging.h>
|
||||
#include <AnimVariant.h>
|
||||
#include <AnimExpression.h>
|
||||
#include <AnimUtil.h>
|
||||
|
||||
#include <../QTestExtensions.h>
|
||||
|
||||
|
@ -315,7 +316,6 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
|
|||
triggers.clear();
|
||||
}
|
||||
|
||||
|
||||
void AnimTests::testAnimPose() {
|
||||
const float PI = (float)M_PI;
|
||||
const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
|
@ -394,3 +394,234 @@ void AnimTests::testAnimPose() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimTests::testExpressionTokenizer() {
|
||||
QString str = "(10 + x) >= 20.1 && (y != !z)";
|
||||
AnimExpression e("x");
|
||||
auto iter = str.cbegin();
|
||||
AnimExpression::Token token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::LeftParen);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Int);
|
||||
QVERIFY(token.intVal == 10);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Plus);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Identifier);
|
||||
QVERIFY(token.strVal == "x");
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::RightParen);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Float);
|
||||
QVERIFY(fabsf(token.floatVal - 20.1f) < 0.0001f);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::And);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::LeftParen);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Identifier);
|
||||
QVERIFY(token.strVal == "y");
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::NotEqual);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Not);
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Identifier);
|
||||
QVERIFY(token.strVal == "z");
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::RightParen);
|
||||
token = e.consumeToken(str, iter);
|
||||
|
||||
str = "true";
|
||||
iter = str.cbegin();
|
||||
token = e.consumeToken(str, iter);
|
||||
QVERIFY(token.type == AnimExpression::Token::Bool);
|
||||
QVERIFY(token.intVal == (int)true);
|
||||
}
|
||||
|
||||
void AnimTests::testExpressionParser() {
|
||||
|
||||
auto vars = AnimVariantMap();
|
||||
vars.set("f", false);
|
||||
vars.set("t", true);
|
||||
vars.set("ten", (int)10);
|
||||
vars.set("twenty", (int)20);
|
||||
vars.set("five", (float)5.0f);
|
||||
vars.set("forty", (float)40.0f);
|
||||
|
||||
AnimExpression e("10");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 10);
|
||||
}
|
||||
|
||||
e = AnimExpression("(10)");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 10);
|
||||
}
|
||||
|
||||
e = AnimExpression("((10))");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 10);
|
||||
}
|
||||
|
||||
e = AnimExpression("12.5");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Float);
|
||||
QVERIFY(e._opCodes[0].floatVal == 12.5f);
|
||||
}
|
||||
|
||||
e = AnimExpression("twenty");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[0].strVal == "twenty");
|
||||
}
|
||||
|
||||
e = AnimExpression("true || false");
|
||||
QVERIFY(e._opCodes.size() == 3);
|
||||
if (e._opCodes.size() == 3) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[0].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[1].intVal == (int)false);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or);
|
||||
}
|
||||
|
||||
e = AnimExpression("true || false && true");
|
||||
QVERIFY(e._opCodes.size() == 5);
|
||||
if (e._opCodes.size() == 5) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[0].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[1].intVal == (int)false);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[2].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::And);
|
||||
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Or);
|
||||
}
|
||||
|
||||
e = AnimExpression("(true || false) && true");
|
||||
QVERIFY(e._opCodes.size() == 5);
|
||||
if (e._opCodes.size() == 5) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[0].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[1].intVal == (int)false);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[3].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::And);
|
||||
}
|
||||
|
||||
e = AnimExpression("!(true || false) && true");
|
||||
QVERIFY(e._opCodes.size() == 6);
|
||||
if (e._opCodes.size() == 6) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[0].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[1].intVal == (int)false);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Not);
|
||||
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY(e._opCodes[4].intVal == (int)true);
|
||||
QVERIFY(e._opCodes[5].type == AnimExpression::OpCode::And);
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_BOOL_EXPR(EXPR) \
|
||||
result = AnimExpression( #EXPR ).evaluate(vars); \
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Bool); \
|
||||
QVERIFY(result.intVal == (int)(EXPR))
|
||||
|
||||
void AnimTests::testExpressionEvaluator() {
|
||||
auto vars = AnimVariantMap();
|
||||
|
||||
bool f = false;
|
||||
bool t = true;
|
||||
int ten = 10;
|
||||
int twenty = 20;
|
||||
float five = 5.0f;
|
||||
float fourty = 40.0f;
|
||||
vars.set("f", f);
|
||||
vars.set("t", t);
|
||||
vars.set("ten", ten);
|
||||
vars.set("twenty", twenty);
|
||||
vars.set("five", five);
|
||||
vars.set("forty", fourty);
|
||||
|
||||
AnimExpression::OpCode result(AnimExpression::OpCode::Int);
|
||||
|
||||
result = AnimExpression("10").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == 10);
|
||||
|
||||
result = AnimExpression("(10)").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == 10);
|
||||
|
||||
TEST_BOOL_EXPR(true);
|
||||
TEST_BOOL_EXPR(false);
|
||||
TEST_BOOL_EXPR(t);
|
||||
TEST_BOOL_EXPR(f);
|
||||
|
||||
TEST_BOOL_EXPR(true || false);
|
||||
TEST_BOOL_EXPR(true || true);
|
||||
TEST_BOOL_EXPR(false || false);
|
||||
TEST_BOOL_EXPR(false || true);
|
||||
|
||||
TEST_BOOL_EXPR(true && false);
|
||||
TEST_BOOL_EXPR(true && true);
|
||||
TEST_BOOL_EXPR(false && false);
|
||||
TEST_BOOL_EXPR(false && true);
|
||||
|
||||
TEST_BOOL_EXPR(true || false && true);
|
||||
TEST_BOOL_EXPR(true || false && false);
|
||||
TEST_BOOL_EXPR(true || true && true);
|
||||
TEST_BOOL_EXPR(true || true && false);
|
||||
TEST_BOOL_EXPR(false || false && true);
|
||||
TEST_BOOL_EXPR(false || false && false);
|
||||
TEST_BOOL_EXPR(false || true && true);
|
||||
TEST_BOOL_EXPR(false || true && false);
|
||||
|
||||
TEST_BOOL_EXPR(true && false || true);
|
||||
TEST_BOOL_EXPR(true && false || false);
|
||||
TEST_BOOL_EXPR(true && true || true);
|
||||
TEST_BOOL_EXPR(true && true || false);
|
||||
TEST_BOOL_EXPR(false && false || true);
|
||||
TEST_BOOL_EXPR(false && false || false);
|
||||
TEST_BOOL_EXPR(false && true || true);
|
||||
TEST_BOOL_EXPR(false && true || false);
|
||||
|
||||
TEST_BOOL_EXPR(t || false);
|
||||
TEST_BOOL_EXPR(t || true);
|
||||
TEST_BOOL_EXPR(f || false);
|
||||
TEST_BOOL_EXPR(f || true);
|
||||
|
||||
TEST_BOOL_EXPR(!true);
|
||||
TEST_BOOL_EXPR(!false);
|
||||
TEST_BOOL_EXPR(!true || true);
|
||||
|
||||
TEST_BOOL_EXPR(!true && !false || !true);
|
||||
TEST_BOOL_EXPR(!true && !false || true);
|
||||
TEST_BOOL_EXPR(!true && false || !true);
|
||||
TEST_BOOL_EXPR(!true && false || true);
|
||||
TEST_BOOL_EXPR(true && !false || !true);
|
||||
TEST_BOOL_EXPR(true && !false || true);
|
||||
TEST_BOOL_EXPR(true && false || !true);
|
||||
TEST_BOOL_EXPR(true && false || true);
|
||||
|
||||
TEST_BOOL_EXPR(!(true && f) || !t);
|
||||
TEST_BOOL_EXPR(!!!(t) && (!!f || true));
|
||||
TEST_BOOL_EXPR(!(true && f) && true);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ private slots:
|
|||
void testVariant();
|
||||
void testAccumulateTime();
|
||||
void testAnimPose();
|
||||
void testExpressionTokenizer();
|
||||
void testExpressionParser();
|
||||
void testExpressionEvaluator();
|
||||
};
|
||||
|
||||
#endif // hifi_AnimTests_h
|
||||
|
|
57
tests/shared/src/GLMHelpersTests.cpp
Normal file
57
tests/shared/src/GLMHelpersTests.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// GLMHelpersTests.cpp
|
||||
// tests/shared/src
|
||||
//
|
||||
// Created by Anthony Thibault on 2015.12.29
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "GLMHelpersTests.h"
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include <../QTestExtensions.h>
|
||||
|
||||
|
||||
QTEST_MAIN(GLMHelpersTests)
|
||||
|
||||
void GLMHelpersTests::testEulerDecomposition() {
|
||||
// quat to euler and back again....
|
||||
|
||||
const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f));
|
||||
const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
|
||||
const float EPSILON = 0.00001f;
|
||||
|
||||
std::vector<glm::quat> quatVec = {
|
||||
glm::quat(),
|
||||
ROT_X_90,
|
||||
ROT_Y_180,
|
||||
ROT_Z_30,
|
||||
ROT_X_90 * ROT_Y_180 * ROT_Z_30,
|
||||
ROT_X_90 * ROT_Z_30 * ROT_Y_180,
|
||||
ROT_Y_180 * ROT_Z_30 * ROT_X_90,
|
||||
ROT_Y_180 * ROT_X_90 * ROT_Z_30,
|
||||
ROT_Z_30 * ROT_X_90 * ROT_Y_180,
|
||||
ROT_Z_30 * ROT_Y_180 * ROT_X_90,
|
||||
};
|
||||
|
||||
for (auto& q : quatVec) {
|
||||
glm::vec3 euler = safeEulerAngles(q);
|
||||
glm::quat r(euler);
|
||||
|
||||
// when the axis and angle are flipped.
|
||||
if (glm::dot(q, r) < 0.0f) {
|
||||
r = -r;
|
||||
}
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(q, r, EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
|
27
tests/shared/src/GLMHelpersTests.h
Normal file
27
tests/shared/src/GLMHelpersTests.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// GLMHelpersTests.h
|
||||
// tests/shared/src
|
||||
//
|
||||
// Created by Anthony thibault on 2015.12.29
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_GLMHelpersTests_h
|
||||
#define hifi_GLMHelpersTests_h
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
class GLMHelpersTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void testEulerDecomposition();
|
||||
};
|
||||
|
||||
float getErrorDifference(const float& a, const float& b);
|
||||
float getErrorDifference(const glm::vec3& a, const glm::vec3& b);
|
||||
|
||||
#endif // hifi_GLMHelpersTest_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// GeometryUtilTests.cpp
|
||||
// tests/physics/src
|
||||
// tests/shared/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2015.07.27
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// GeometryUtilTests.h
|
||||
// tests/physics/src
|
||||
// tests/shared/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AngularConstraintTests_h
|
||||
#define hifi_AngularConstraintTests_h
|
||||
#ifndef hifi_GeometryUtilTests_h
|
||||
#define hifi_GeometryUtilTests_h
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <glm/glm.hpp>
|
||||
|
@ -26,4 +26,4 @@ private slots:
|
|||
float getErrorDifference(const float& a, const float& b);
|
||||
float getErrorDifference(const glm::vec3& a, const glm::vec3& b);
|
||||
|
||||
#endif // hifi_AngularConstraintTests_h
|
||||
#endif // hifi_GeometryUtilTests_h
|
||||
|
|
BIN
tools/nsis/installer_vertical.bmp
Normal file
BIN
tools/nsis/installer_vertical.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
|
@ -1,164 +1,220 @@
|
|||
!include LogicLib.nsh
|
||||
!include x64.nsh
|
||||
!include MUI2.nsh
|
||||
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
; Source Directory Definition
|
||||
;
|
||||
; installer_srcdir = Source directory for Interface
|
||||
; scripts_srcdir = Source directory for JS scripts
|
||||
|
||||
!define installer_srcdir "$%INSTALLER_SOURCE_DIR%"
|
||||
!define scripts_srcdir "$%INSTALLER_SCRIPTS_DIR%"
|
||||
|
||||
; Install Directories, Icons and Registry entries
|
||||
;
|
||||
; setup = Name of the installer executable that will be produced
|
||||
; uninstaller = Name of the uninstaller executable
|
||||
; company = String to use for company name, includes build type suffix [eg. High Fidelity - PR] for non-release installers
|
||||
; install_directory = Subdirectory where this specific version will be installed, in the case of dev and pr builds, its a
|
||||
; unique subdirectory inside a company parent [eg. \High Fidelity - PR\1234\ ]
|
||||
|
||||
!define srcdir "$%INSTALLER_SOURCE_DIR%"
|
||||
!define setup "$%INSTALLER_NAME%"
|
||||
!define scriptsdir "$%INSTALLER_SCRIPTS_DIR%"
|
||||
!define uninstaller "uninstall.exe"
|
||||
!define company "$%INSTALLER_COMPANY%"
|
||||
!define install_directory "$%INSTALLER_DIRECTORY%"
|
||||
|
||||
; Executables and icons for GUI applications that will be added as shortcuts.
|
||||
!define interface_exec "interface.exe"
|
||||
!define stack_manager_exec "stack-manager.exe"
|
||||
!define interface_icon "interface.ico"
|
||||
!define stack_manager_icon "stack-manager.ico"
|
||||
!define regkey "Software\${company}"
|
||||
!define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${company}"
|
||||
!define install_dir_company "$PROGRAMFILES64\${install_directory}"
|
||||
|
||||
; Registry entries
|
||||
!define regkey "Software\${install_directory}"
|
||||
!define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${install_directory}"
|
||||
!define instdir "$PROGRAMFILES64\${install_directory}"
|
||||
|
||||
; Start Menu program group
|
||||
!define startmenu_company "$SMPROGRAMS\${install_directory}"
|
||||
!define uninstaller "uninstall.exe"
|
||||
|
||||
;--------------------------------
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
; Local Variables and Other Options
|
||||
|
||||
XPStyle on
|
||||
var ChosenInstallDir
|
||||
|
||||
SetCompressor bzip2
|
||||
ShowInstDetails hide
|
||||
ShowUninstDetails hide
|
||||
|
||||
Name "${company}"
|
||||
Caption "${company}"
|
||||
|
||||
!ifdef icon
|
||||
Icon "${icon}"
|
||||
!endif
|
||||
|
||||
OutFile "${setup}"
|
||||
|
||||
AutoCloseWindow true
|
||||
ShowInstDetails show
|
||||
SetDateSave on
|
||||
SetDatablockOptimize on
|
||||
CRCCheck on
|
||||
SilentInstall normal
|
||||
Icon "${installer_srcdir}\${interface_icon}"
|
||||
UninstallIcon "${installer_srcdir}\${interface_icon}"
|
||||
UninstallText "This will uninstall ${company}."
|
||||
Name "${company}"
|
||||
Caption "${company}"
|
||||
OutFile "${setup}"
|
||||
|
||||
InstallDir "${install_dir_company}"
|
||||
InstallDirRegKey HKLM "${regkey}" ""
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
; Components
|
||||
|
||||
; Page components
|
||||
Page directory
|
||||
Page components
|
||||
Page instfiles
|
||||
|
||||
UninstPage uninstConfirm
|
||||
UninstPage instfiles
|
||||
|
||||
;--------------------------------
|
||||
|
||||
AutoCloseWindow true
|
||||
ShowInstDetails show
|
||||
|
||||
|
||||
!ifdef screenimage
|
||||
|
||||
; set up background image
|
||||
; uses BgImage plugin
|
||||
|
||||
Function .onGUIInit
|
||||
; extract background BMP into temp plugin directory
|
||||
InitPluginsDir
|
||||
File /oname=$PLUGINSDIR\1.bmp "${screenimage}"
|
||||
|
||||
BgImage::SetBg /NOUNLOAD /FILLSCREEN $PLUGINSDIR\1.bmp
|
||||
BgImage::Redraw /NOUNLOAD
|
||||
FunctionEnd
|
||||
|
||||
Function .onGUIEnd
|
||||
; Destroy must not have /NOUNLOAD so NSIS will be able to unload and delete BgImage before it exits
|
||||
BgImage::Destroy
|
||||
FunctionEnd
|
||||
|
||||
!endif
|
||||
|
||||
; Optional Component Selection
|
||||
Section /o "DDE Face Recognition" SEC01
|
||||
SetOutPath "$INSTDIR"
|
||||
CreateDirectory $INSTDIR\dde
|
||||
NSISdl::download "https://s3-us-west-1.amazonaws.com/hifi-production/optionals/dde-installer.exe" "$INSTDIR\dde-installer.exe"
|
||||
ExecWait '"$INSTDIR\dde-installer.exe" /q:a /t:"$INSTDIR\dde"'
|
||||
Section /o "DDE Face Recognition" "DDE"
|
||||
SetOutPath "$ChosenInstallDir"
|
||||
CreateDirectory $ChosenInstallDir\dde
|
||||
NSISdl::download "https://s3-us-west-1.amazonaws.com/hifi-production/optionals/dde-installer.exe" "$ChosenInstallDir\dde-installer.exe"
|
||||
ExecWait '"$ChosenInstallDir\dde-installer.exe" /q:a /t:"$ChosenInstallDir\dde"'
|
||||
SectionEnd
|
||||
|
||||
; beginning (invisible) section
|
||||
Section "Registry Entries and Procotol Handler" SEC02
|
||||
Section /o "Default Content Set" "CONTENT"
|
||||
SetOutPath "$ChosenInstallDir\resources"
|
||||
NSISdl::download "https://s3-us-west-1.amazonaws.com/hifi-production/content/temp.exe" "$ChosenInstallDir\content.exe"
|
||||
ExecWait '"$ChosenInstallDir\content.exe" /S'
|
||||
Delete "$ChosenInstallDir\content.exe"
|
||||
SectionEnd
|
||||
|
||||
Section "Registry Entries and Procotol Handler" "REGISTRY"
|
||||
SetRegView 64
|
||||
SectionIn RO
|
||||
|
||||
WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR"
|
||||
WriteRegStr HKLM "${uninstkey}" "DisplayName" "${company} (remove only)"
|
||||
WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"'
|
||||
WriteRegStr HKCR "${company}\Shell\open\command\" "" '"$INSTDIR\${interface_exec} "%1"'
|
||||
WriteRegStr HKCR "${company}\DefaultIcon" "" "$INSTDIR\${interface_icon}"
|
||||
WriteRegStr HKLM "${regkey}" "Install_Dir" "$ChosenInstallDir"
|
||||
WriteRegStr HKLM "${uninstkey}" "DisplayName" "${install_directory} (remove only)"
|
||||
WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$ChosenInstallDir\${uninstaller}"'
|
||||
WriteRegStr HKCR "${company}\Shell\open\command\" "" '"$ChosenInstallDir\${interface_exec} "%1"'
|
||||
WriteRegStr HKCR "${company}\DefaultIcon" "" "$ChosenInstallDir\${interface_icon}"
|
||||
|
||||
; hifi:// protocol handler registry entries
|
||||
WriteRegStr HKCR 'hifi' '' 'URL:Alert Protocol'
|
||||
WriteRegStr HKCR 'hifi' 'URL Protocol' ''
|
||||
WriteRegStr HKCR 'hifi\DefaultIcon' '' '$INSTDIR\${interface_icon},1'
|
||||
WriteRegStr HKCR 'hifi\shell\open\command' '' '$INSTDIR\${interface_exec} --url "%1"'
|
||||
WriteRegStr HKCR 'hifi\DefaultIcon' '' '$ChosenInstallDir\${interface_icon},1'
|
||||
WriteRegStr HKCR 'hifi\shell\open\command' '' '$ChosenInstallDir\${interface_exec} --url "%1"'
|
||||
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; package all files, recursively, preserving attributes
|
||||
; assume files are in the correct places
|
||||
File /r "${srcdir}\"
|
||||
File /a "${srcdir}\${interface_icon}"
|
||||
File /a "${srcdir}\${stack_manager_icon}"
|
||||
; any application-specific files
|
||||
!ifdef files
|
||||
!include "${files}"
|
||||
!endif
|
||||
WriteUninstaller "${uninstaller}"
|
||||
Exec '"$INSTDIR\2013_vcredist_x64.exe" /q /norestart'
|
||||
Exec '"$INSTDIR\2010_vcredist_x86.exe" /q /norestart'
|
||||
SetOutPath $ChosenInstallDir
|
||||
WriteUninstaller "$ChosenInstallDir\${uninstaller}"
|
||||
Exec '"$ChosenInstallDir\2013_vcredist_x64.exe" /q /norestart'
|
||||
Exec '"$ChosenInstallDir\2010_vcredist_x86.exe" /q /norestart'
|
||||
SectionEnd
|
||||
|
||||
; create shortcuts
|
||||
Section "Start Menu Shortcuts" SEC03
|
||||
|
||||
Section "Base Files" "BASE"
|
||||
SectionIn RO
|
||||
SetOutPath $ChosenInstallDir
|
||||
File /r /x assignment-client.* /x domain-server.* /x interface.* /x stack-manager.* "${installer_srcdir}\"
|
||||
SectionEnd
|
||||
|
||||
; This should install the shortcuts for "All Users"
|
||||
Section "HighFidelity Interface" "CLIENT"
|
||||
SetOutPath $ChosenInstallDir
|
||||
File /a "${installer_srcdir}\interface.*"
|
||||
File /a "${installer_srcdir}\${interface_icon}"
|
||||
SectionEnd
|
||||
|
||||
Section "HighFidelity Server" "SERVER"
|
||||
SetOutPath $ChosenInstallDir
|
||||
File /a "${installer_srcdir}\stack-manager.*"
|
||||
File /a "${installer_srcdir}\domain-server.*"
|
||||
File /a "${installer_srcdir}\assignment-client.*"
|
||||
File /a "${installer_srcdir}\${stack_manager_icon}"
|
||||
SectionEnd
|
||||
|
||||
Section "Start Menu Shortcuts" "SHORTCUTS"
|
||||
SetShellVarContext all
|
||||
CreateDirectory "${startmenu_company}"
|
||||
SetOutPath $INSTDIR ; for working directory
|
||||
CreateShortCut "${startmenu_company}\Interface.lnk" "$INSTDIR\${interface_exec}" "" "$INSTDIR\${interface_icon}"
|
||||
CreateShortCut "${startmenu_company}\Stack Manager.lnk" "$INSTDIR\${stack_manager_exec}" "" "$INSTDIR\${stack_manager_icon}"
|
||||
CreateShortCut "${startmenu_company}\Uninstall ${company}.lnk" "$INSTDIR\${uninstaller}"
|
||||
SetOutPath $ChosenInstallDir
|
||||
CreateShortCut "${startmenu_company}\Client.lnk" "$ChosenInstallDir\${interface_exec}" "" "$ChosenInstallDir\${interface_icon}"
|
||||
CreateShortCut "${startmenu_company}\Home Server.lnk" "$ChosenInstallDir\${stack_manager_exec}" "" "$ChosenInstallDir\${stack_manager_icon}"
|
||||
CreateShortCut "${startmenu_company}\Uninstall ${company}.lnk" "$ChosenInstallDir\${uninstaller}"
|
||||
SectionEnd
|
||||
|
||||
; Uninstaller
|
||||
; All section names prefixed by "Un" will be in the uninstaller
|
||||
|
||||
UninstallText "This will uninstall ${company}."
|
||||
|
||||
!ifdef icon
|
||||
UninstallIcon "${interface_icon}"
|
||||
!endif
|
||||
|
||||
Section "Uninstall" SEC04
|
||||
|
||||
SectionIn RO
|
||||
|
||||
; Explicitly remove all added shortcuts
|
||||
SetShellVarContext all
|
||||
DELETE "${startmenu_company}\Interface.lnk"
|
||||
DELETE "${startmenu_company}\Stack Manager.lnk"
|
||||
DELETE "${startmenu_company}\Uninstall ${company}.lnk"
|
||||
|
||||
RMDIR "${startmenu_company}"
|
||||
|
||||
RMDIR /r "$INSTDIR"
|
||||
; This should remove the High Fidelity folder in Program Files if it's empty
|
||||
RMDIR "${install_dir_company}"
|
||||
|
||||
!ifdef unfiles
|
||||
!include "${unfiles}"
|
||||
!endif
|
||||
; It's good practice to put the registry key removal at the very end
|
||||
Section "Uninstall"
|
||||
SetRegView 64
|
||||
Delete "$INSTDIR\${uninstaller}"
|
||||
Delete "$SMSTARTUP\High Fidelity Home Server.lnk"
|
||||
RMDir /r "$INSTDIR"
|
||||
RMDir /r "${startmenu_company}"
|
||||
RMDir /r "$0"
|
||||
DeleteRegKey HKLM "${uninstkey}"
|
||||
DeleteRegKey HKLM "${regkey}"
|
||||
DeleteRegKey HKCR "${company}"
|
||||
DeleteRegKey HKCR 'hifi'
|
||||
SectionEnd
|
||||
SectionEnd
|
||||
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
; Functions
|
||||
|
||||
Function .onInit
|
||||
StrCpy $ChosenInstallDir "${instdir}"
|
||||
SectionSetText ${REGISTRY} ""
|
||||
SectionSetText ${SHORTCUTS} ""
|
||||
SectionSetText ${BASE} ""
|
||||
FunctionEnd
|
||||
|
||||
var ServerCheckBox
|
||||
var ServerCheckBox_state
|
||||
var RunOnStartupCheckBox
|
||||
var RunOnStartupCheckBox_state
|
||||
|
||||
Function RunCheckboxes
|
||||
${If} ${SectionIsSelected} ${SERVER}
|
||||
${NSD_CreateCheckbox} 36.2% 56% 100% 10u "&Start Home Server"
|
||||
Pop $ServerCheckBox
|
||||
SetCtlColors $ServerCheckBox "" "ffffff"
|
||||
${NSD_CreateCheckbox} 36.2% 65% 100% 10u "&Always launch your Home Server on startup"
|
||||
Pop $RunOnStartupCheckBox
|
||||
SetCtlColors $RunOnStartupCheckBox "" "ffffff"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function HandleCheckBoxes
|
||||
${If} ${SectionIsSelected} ${SERVER}
|
||||
${NSD_GetState} $ServerCheckBox $ServerCheckBox_state
|
||||
${If} $ServerCheckBox_state == ${BST_CHECKED}
|
||||
SetOutPath $ChosenInstallDir
|
||||
ExecShell "" '"$ChosenInstallDir\stack-manager.exe"'
|
||||
${EndIf}
|
||||
${NSD_GetState} $RunOnStartupCheckBox $RunOnStartupCheckBox_state
|
||||
${If} $ServerCheckBox_state == ${BST_CHECKED}
|
||||
CreateShortCut "$SMSTARTUP\High Fidelity Home Server.lnk" "$ChosenInstallDir\${stack_manager_exec}" "" "$ChosenInstallDir\${stack_manager_icon}"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
; User interface macros and other definitions
|
||||
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "installer_vertical.bmp"
|
||||
!define MUI_WELCOMEPAGE_TITLE "High Fidelity - Integrated Installer"
|
||||
!define MUI_WELCOMEPAGE_TEXT "Welcome to High Fidelity! This installer includes both High Fidelity Interface for VR access as well as the High Fidelity Server for you to host your own domain in the metaverse."
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
|
||||
!define MUI_PAGE_HEADER_TEXT "Please select the components you want to install"
|
||||
!define MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_INFO "Hover over a component for a brief description"
|
||||
!insertmacro MUI_PAGE_COMPONENTS
|
||||
|
||||
!define MUI_DIRECTORYPAGE_VARIABLE $ChosenInstallDir
|
||||
!define MUI_PAGE_HEADER_TEXT "High Fidelity"
|
||||
!define MUI_PAGE_HEADER_SUBTEXT ""
|
||||
!define MUI_DIRECTORYPAGE_TEXT_TOP "Choose a location for your High Fidelity installation."
|
||||
!define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Install Directory"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_SHOW RunCheckboxes
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE HandleCheckboxes
|
||||
!define MUI_FINISHPAGE_RUN_NOTCHECKED
|
||||
!define MUI_FINISHPAGE_RUN "$ChosenInstallDir\interface.exe"
|
||||
!define MUI_FINISHPAGE_RUN_TEXT "Start High Fidelity Interface"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${DDE} "DDE enables facial gesture recognition using a standard 2D webcam"
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${CONTENT} "Demo content set for your home server"
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${CLIENT} "The High Fidelity Interface Client for connection to domains in the metaverse."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SERVER} "The High Fidelity Server - run your own home domain"
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
|
Loading…
Reference in a new issue