Merge remote-tracking branch 'leo/master' into console

This commit is contained in:
Stephen Birarda 2016-01-04 09:57:55 -08:00
commit 02069bfcd2
41 changed files with 2857 additions and 543 deletions

56
cmake/externals/neuron/CMakeLists.txt vendored Normal file
View 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()

View file

@ -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()

View file

@ -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()

View file

@ -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")

View 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()

View 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)

View file

@ -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 ()

View file

@ -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() {

View 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);
}

View file

@ -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);
}
});

View file

@ -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();
});
});

View file

@ -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

View file

@ -0,0 +1,7 @@
{
"name": "Neuron to Standard",
"channels": [
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
]
}

View file

@ -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));
}
}
}
}
}

View file

@ -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();
}

View 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

View 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

View file

@ -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();

View file

@ -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.)

View file

@ -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) {

View file

@ -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
};
}

View file

@ -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);
}

View file

@ -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()) {

View file

@ -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);

View file

@ -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

View 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()

View 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());
}

View 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

View 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"

View file

@ -0,0 +1 @@
{}

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -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;
}

View file

@ -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);
}

View file

@ -27,6 +27,9 @@ private slots:
void testVariant();
void testAccumulateTime();
void testAnimPose();
void testExpressionTokenizer();
void testExpressionParser();
void testExpressionEvaluator();
};
#endif // hifi_AnimTests_h

View 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);
}
}

View 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

View file

@ -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.

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View file

@ -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"