merge from upstream

This commit is contained in:
Seth Alves 2015-10-30 19:17:43 -07:00
commit 896ed3927c
199 changed files with 9152 additions and 4020 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -176,6 +176,7 @@ function MyController(hand, triggerAction) {
this.state = STATE_OFF;
this.pointer = null; // entity-id of line object
this.triggerValue = 0; // rolling average of trigger value
this.rawTriggerValue = 0;
var _this = this;
@ -272,12 +273,17 @@ function MyController(hand, triggerAction) {
this.pointer = null;
};
this.updateSmoothedTrigger = function() {
var triggerValue = Controller.getActionValue(this.triggerAction);
this.eitherTrigger = function (value) {
_this.rawTriggerValue = value;
};
this.updateSmoothedTrigger = function () {
var triggerValue = this.rawTriggerValue;
// smooth out trigger value
this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
}
};
this.triggerSmoothedSqueezed = function() {
return this.triggerValue > TRIGGER_ON_VALUE;
@ -287,8 +293,8 @@ function MyController(hand, triggerAction) {
return this.triggerValue < TRIGGER_OFF_VALUE;
};
this.triggerSqueezed = function() {
var triggerValue = Controller.getActionValue(this.triggerAction);
this.triggerSqueezed = function() {
var triggerValue = this.rawTriggerValue;
return triggerValue > TRIGGER_ON_VALUE;
};
@ -430,12 +436,12 @@ function MyController(hand, triggerAction) {
};
this.distanceHolding = function() {
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity",
"ignoreForCollisions",
"collisionsWillMove"]);
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
"gravity", "ignoreForCollisions",
var now = Date.now();
// add the action and initialize some variables
@ -482,8 +488,9 @@ function MyController(hand, triggerAction) {
}
var handPosition = this.getHandPosition();
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
@ -633,7 +640,7 @@ function MyController(hand, triggerAction) {
}
this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip);
this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;;
this.currentObjectTime = Date.now();
};
@ -651,7 +658,7 @@ function MyController(hand, triggerAction) {
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
// from the palm.
var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
var now = Date.now();
var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
@ -899,8 +906,16 @@ function MyController(hand, triggerAction) {
};
}
var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT);
var leftController = new MyController(LEFT_HAND, Controller.Standard.LT);
var MAPPING_NAME = "com.highfidelity.handControllerGrab";
var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger);
mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger);
Controller.enableMapping(MAPPING_NAME);
function update() {
rightController.update();
@ -910,6 +925,7 @@ function update() {
function cleanup() {
rightController.cleanup();
leftController.cleanup();
Controller.disableMapping(MAPPING_NAME);
}
Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,102 @@
//
// handPosesDebug.js
// examples
//
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
function makeSphere(color) {
var SPHERE_SIZE = 0.05;
var sphere = Overlays.addOverlay("sphere", {
position: { x: 0, y: 0, z: 0 },
size: SPHERE_SIZE,
color: color,
alpha: 1.0,
solid: true,
visible: true,
});
return sphere;
}
var NUM_HANDS = 2;
var NUM_SPHERES_PER_HAND = 2;
var LEFT_HAND = 0;
var RIGHT_HAND = 1;
var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ];
function index(handNum, indexNum) {
return handNum * NUM_HANDS + indexNum;
}
var app = {};
function setup() {
app.spheres = new Array();
for (var h = 0; h < NUM_HANDS; h++) {
for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) {
var i = index(h, s);
app.spheres[i] = makeSphere(COLORS[h]);
print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i]));
}
}
}
function updateHand(handNum, deltaTime) {
var pose;
var handName = "right";
if (handNum == LEFT_HAND) {
pose = MyAvatar.getLeftHandPose();
handName = "left";
} else {
pose = MyAvatar.getRightHandPose();
handName = "right";
}
if (pose.valid) {
//print(handName + " hand moving" + JSON.stringify(pose));
Overlays.editOverlay(app.spheres[index(handNum, 0)], {
position: pose.translation,
visible: true,
});
var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation);
Overlays.editOverlay(app.spheres[index(handNum, 1)], {
position: vpos,
visible: true,
});
} else {
Overlays.editOverlay(app.spheres[index(handNum, 0)], {
visible: false
});
Overlays.editOverlay(app.spheres[index(handNum, 1)], {
visible: false
});
}
}
function update(deltaTime) {
updateHand(LEFT_HAND, deltaTime);
updateHand(RIGHT_HAND, deltaTime);
}
function scriptEnding() {
print("Removing spheres = " + JSON.stringify(app.spheres));
for (var i = 0; i < app.spheres.length; i++) {
Overlays.deleteOverlay(app.spheres[i]);
}
}
setup();
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,26 @@
//
// lineExample.js
// examples/example
//
// Created by Ryan Huffman on October 27, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../libraries/line.js");
var basePosition = MyAvatar.position;
var color = { red: 128, green: 220, blue: 190 };
var strokeWidth = 0.01;
var line = new InfiniteLine(basePosition, color, 20);
for (var i = 0; i < (16 * Math.PI); i += 0.05) {
var x = 0
var y = 0.25 * Math.sin(i);
var z = i / 10;
var position = Vec3.sum(basePosition, { x: x, y: y, z: z });
line.enqueuePoint(position, strokeWidth);
}

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -104,8 +104,8 @@ endif()
link_hifi_libraries(shared octree environment gpu gl procedural model render
fbx networking model-networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater
plugins display-plugins input-plugins)
render-utils entities-renderer ui auto-updater
controllers plugins display-plugins input-plugins )
#fixme find a way to express faceshift as a plugin
target_bullet()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,12 +14,20 @@
#include <QtCore/QObject>
#include <input-plugins/UserInputMapper.h>
#include <controllers/UserInputMapper.h>
#include <controllers/ScriptingInterface.h>
#include <HFActionEvent.h>
#include <KeyEvent.h>
#include <MouseEvent.h>
#include <SpatialEvent.h>
#include <TouchEvent.h>
#include <WheelEvent.h>
class ScriptEngine;
#include <AbstractControllerScriptingInterface.h>
class PalmData;
class InputController : public AbstractInputController {
class InputController : public controller::InputController {
Q_OBJECT
public:
@ -50,125 +58,77 @@ signals:
/// handles scripting of input controller commands from JS
class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
class ControllerScriptingInterface : public controller::ScriptingInterface {
Q_OBJECT
public:
ControllerScriptingInterface();
virtual void registerControllerTypes(ScriptEngine* engine);
void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
virtual ~ControllerScriptingInterface() {}
void emitKeyPressEvent(QKeyEvent* event);
void emitKeyReleaseEvent(QKeyEvent* event);
void handleMetaEvent(HFMetaEvent* event);
void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }
void emitTouchBeginEvent(const TouchEvent& event);
void emitTouchEndEvent(const TouchEvent& event);
void emitTouchUpdateEvent(const TouchEvent& event);
void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
void emitWheelEvent(QWheelEvent* event);
bool isKeyCaptured(QKeyEvent* event) const;
bool isKeyCaptured(const KeyEvent& event) const;
bool isMouseCaptured() const { return _mouseCaptured; }
bool isTouchCaptured() const { return _touchCaptured; }
bool isWheelCaptured() const { return _wheelCaptured; }
bool areActionsCaptured() const { return _actionsCaptured; }
bool isJoystickCaptured(int joystickIndex) const;
void updateInputControllers();
public slots:
Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions();
Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel);
Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device);
Q_INVOKABLE virtual QString getDeviceName(unsigned int device);
Q_INVOKABLE virtual float getActionValue(int action);
Q_INVOKABLE virtual void resetDevice(unsigned int device);
Q_INVOKABLE virtual void resetAllDeviceBindings();
Q_INVOKABLE virtual int findDevice(QString name);
Q_INVOKABLE virtual QVector<QString> getDeviceNames();
Q_INVOKABLE virtual int findAction(QString actionName);
Q_INVOKABLE virtual QVector<QString> getActionNames() const;
virtual bool isPrimaryButtonPressed() const;
virtual glm::vec2 getPrimaryJoystickPosition() const;
virtual int getNumberOfButtons() const;
virtual bool isButtonPressed(int buttonIndex) const;
virtual int getNumberOfTriggers() const;
virtual float getTriggerValue(int triggerIndex) const;
virtual int getNumberOfJoysticks() const;
virtual glm::vec2 getJoystickPosition(int joystickIndex) const;
virtual int getNumberOfSpatialControls() const;
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const;
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const;
virtual glm::vec3 getSpatialControlRawAngularVelocity(int controlIndex) const;
virtual void captureKeyEvents(const KeyEvent& event);
virtual void releaseKeyEvents(const KeyEvent& event);
virtual void captureMouseEvents() { _mouseCaptured = true; }
virtual void releaseMouseEvents() { _mouseCaptured = false; }
virtual void captureTouchEvents() { _touchCaptured = true; }
virtual void releaseTouchEvents() { _touchCaptured = false; }
virtual void captureWheelEvents() { _wheelCaptured = true; }
virtual void releaseWheelEvents() { _wheelCaptured = false; }
virtual void captureActionEvents() { _actionsCaptured = true; }
virtual void releaseActionEvents() { _actionsCaptured = false; }
virtual void captureJoystick(int joystickIndex);
virtual void releaseJoystick(int joystickIndex);
virtual glm::vec2 getViewportDimensions() const;
/// Factory to create an InputController
virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker);
virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker);
virtual void releaseInputController(controller::InputController::Pointer input);
virtual void releaseInputController(AbstractInputController* input);
signals:
void keyPressEvent(const KeyEvent& event);
void keyReleaseEvent(const KeyEvent& event);
void actionStartEvent(const HFActionEvent& event);
void actionEndEvent(const HFActionEvent& event);
void backStartEvent();
void backEndEvent();
void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0);
void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0);
void touchBeginEvent(const TouchEvent& event);
void touchEndEvent(const TouchEvent& event);
void touchUpdateEvent(const TouchEvent& event);
void wheelEvent(const WheelEvent& event);
private:
QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript
const PalmData* getPrimaryPalm() const;
const PalmData* getPalm(int palmIndex) const;
int getNumberOfActivePalms() const;
const PalmData* getActivePalm(int palmIndex) const;
bool _mouseCaptured;
bool _touchCaptured;
bool _wheelCaptured;
bool _actionsCaptured;
QMultiMap<int,KeyEvent> _capturedKeys;
QSet<int> _capturedJoysticks;
typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap;
using InputKey = controller::InputController::Key;
using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>;
InputControllerMap _inputControllers;
void wireUpControllers(ScriptEngine* engine);
};
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip

View file

@ -27,7 +27,7 @@
#include "Application.h"
#include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here
#include <input-plugins/InputDevice.h>
#include <controllers/InputDevice.h>
// Used to animate the magnification windows
@ -320,8 +320,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
// Only render the hand pointers if the EnableHandMouseInput is enabled
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
PalmData& palm = myAvatar->getHand()->getPalms()[i];
auto palms = myAvatar->getHand()->getCopyOfPalms();
for (const auto& palm : palms) {
if (palm.isActive()) {
glm::vec2 polar = getPolarCoordinates(palm);
// Convert to quaternion
@ -446,6 +446,7 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
}
// FIXME - this is old code that likely needs to be removed and/or reworked to support the new input control model
void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
@ -455,23 +456,24 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
auto palms = handData->getCopyOfPalms();
for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
const int index = palmIndex - 1;
const PalmData* palmData = NULL;
if (palmIndex >= handData->getPalms().size()) {
if (palmIndex >= palms.size()) {
return;
}
if (handData->getPalms()[palmIndex].isActive()) {
palmData = &handData->getPalms()[palmIndex];
if (palms[palmIndex].isActive()) {
palmData = &palms[palmIndex];
} else {
continue;
}
int controllerButtons = palmData->getControllerButtons();
int controllerButtons = 0;
//Check for if we should toggle or drag the magnification window
if (controllerButtons & BUTTON_3) {
@ -521,7 +523,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
// Get the pixel range over which the xAngle and yAngle are scaled
float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult();
float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);

View file

@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() {
ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());
#endif
ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed());
ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed());
// LOD items
auto lodManager = DependencyManager::get<LODManager>();
@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() {
qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
auto audio = DependencyManager::get<AudioClient>();
MixedProcessedAudioStream& stream = audio->getReceivedAudioStream();

View file

@ -0,0 +1,121 @@
//
// AnimVariantMap.cpp
// library/animation
//
// Created by Howard Stearns on 10/15/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 <QScriptEngine>
#include <QScriptValueIterator>
#include <QThread>
#include <RegisteredMetaTypes.h>
#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap
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();
Q_ASSERT(false);
return QScriptValue();
}
QScriptValue target = engine->newObject();
auto setOne = [&] (const QString& name, const AnimVariant& value) {
switch (value.getType()) {
case AnimVariant::Type::Bool:
target.setProperty(name, value.getBool());
break;
case AnimVariant::Type::Int:
target.setProperty(name, value.getInt());
break;
case AnimVariant::Type::Float:
target.setProperty(name, value.getFloat());
break;
case AnimVariant::Type::String:
target.setProperty(name, value.getString());
break;
case AnimVariant::Type::Vec3:
target.setProperty(name, vec3toScriptValue(engine, value.getVec3()));
break;
case AnimVariant::Type::Quat:
target.setProperty(name, quatToScriptValue(engine, value.getQuat()));
break;
default:
// Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now.
assert("AnimVariant::Type" == "valid");
}
};
if (useNames) { // copy only the requested names
for (const QString& name : names) {
auto search = _map.find(name);
if (search != _map.end()) { // scripts are allowed to request names that do not exist
setOne(name, search->second);
}
}
} else { // copy all of them
for (auto& pair : _map) {
setOne(pair.first, pair.second);
}
}
return target;
}
void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
for (auto& pair : other._map) {
_map[pair.first] = pair.second;
}
}
void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
if (QThread::currentThread() != source.engine()->thread()) {
qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread();
Q_ASSERT(false);
return;
}
// POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types.
// Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here,
// we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if
// the type is not known. One problem with that is that there is no checking that two different script use the same name differently.
QScriptValueIterator property(source);
// Note: QScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain.
while (property.hasNext()) {
property.next();
QScriptValue value = property.value();
if (value.isBool()) {
set(property.name(), value.toBool());
} else if (value.isString()) {
set(property.name(), value.toString());
} else if (value.isNumber()) {
int asInteger = value.toInt32();
float asFloat = value.toNumber();
if (asInteger == asFloat) {
set(property.name(), asInteger);
} else {
set(property.name(), asFloat);
}
} else { // Try to get x,y,z and possibly w
if (value.isObject()) {
QScriptValue x = value.property("x");
if (x.isNumber()) {
QScriptValue y = value.property("y");
if (y.isNumber()) {
QScriptValue z = value.property("z");
if (z.isNumber()) {
QScriptValue w = value.property("w");
if (w.isNumber()) {
set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber()));
} else {
set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber()));
}
continue; // we got either a vector or quaternion object, so don't fall through to warning
}
}
}
}
qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name();
Q_ASSERT(false);
}
}
}

View file

@ -12,11 +12,14 @@
#define hifi_AnimVariant_h
#include <cassert>
#include <functional>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include <map>
#include <set>
#include <QScriptValue>
#include "AnimationLogging.h"
#include "StreamUtils.h"
class AnimVariant {
public:
@ -58,8 +61,9 @@ public:
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); return _val.intVal; }
float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; }
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 glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }
@ -156,8 +160,15 @@ public:
void setTrigger(const QString& key) { _triggers.insert(key); }
void clearTriggers() { _triggers.clear(); }
void clearMap() { _map.clear(); }
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
// 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.)
void animVariantMapFromScriptValue(const QScriptValue& object);
void copyVariantsFrom(const AnimVariantMap& other);
#ifdef NDEBUG
void dump() const {
qCDebug(animation) << "AnimVariantMap =";
@ -196,4 +207,8 @@ protected:
std::set<QString> _triggers;
};
typedef std::function<void(QScriptValue)> AnimVariantResultHandler;
Q_DECLARE_METATYPE(AnimVariantResultHandler);
Q_DECLARE_METATYPE(AnimVariantMap)
#endif // hifi_AnimVariant_h

View file

@ -13,6 +13,7 @@
#include <glm/gtx/vector_angle.hpp>
#include <queue>
#include <QScriptValueIterator>
#include <NumericalConstants.h>
#include <DebugDraw.h>
@ -576,6 +577,71 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_lastPosition = worldPosition;
}
// Allow script to add/remove handlers and report results, from within their thread.
QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread
QMutexLocker locker(&_stateMutex);
// Find a safe id, even if there are lots of many scripts add and remove handlers repeatedly.
while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap.
_nextStateHandlerId++;
}
StateHandler& data = _stateHandlers[_nextStateHandlerId];
data.function = handler;
data.useNames = propertiesList.isArray();
if (data.useNames) {
data.propertyNames = propertiesList.toVariant().toStringList();
}
return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler
}
void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread
QMutexLocker locker(&_stateMutex);
_stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present. 0 is unused
}
void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script
QMutexLocker locker(&_stateMutex);
auto found = _stateHandlers.find(identifier);
if (found == _stateHandlers.end()) {
return; // Don't use late-breaking results that got reported after the handler was removed.
}
found.value().results.animVariantMapFromScriptValue(result); // Into our own copy.
}
void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread)
QMutexLocker locker(&_stateMutex);
// It might pay to produce just one AnimVariantMap copy here, with a union of all the requested propertyNames,
// rather than having each callAnimationStateHandler invocation make its own copy.
// However, that copying is done on the script's own time rather than ours, so even if it's less cpu, it would be more
// work on the avatar update thread (which is possibly the main thread).
for (auto data = _stateHandlers.begin(); data != _stateHandlers.end(); data++) {
// call out:
int identifier = data.key();
StateHandler& value = data.value();
QScriptValue& function = value.function;
auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us.
animationStateHandlerResult(identifier, result);
};
// invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture
// the state of _animVars and allow continued changes to _animVars in this thread without conflict.
QMetaObject::invokeMethod(function.engine(), "callAnimationStateHandler", Qt::QueuedConnection,
Q_ARG(QScriptValue, function),
Q_ARG(AnimVariantMap, _animVars),
Q_ARG(QStringList, value.propertyNames),
Q_ARG(bool, value.useNames),
Q_ARG(AnimVariantResultHandler, handleResult));
// It turns out that, for thread-safety reasons, ScriptEngine::callAnimationStateHandler will invoke itself if called from other
// than the script thread. Thus the above _could_ be replaced with an ordinary call, which will then trigger the same
// invokeMethod as is done explicitly above. However, the script-engine library depends on this animation library, not vice versa.
// We could create an AnimVariantCallingMixin class in shared, with an abstract virtual slot
// AnimVariantCallingMixin::callAnimationStateHandler (and move AnimVariantMap/AnimVaraintResultHandler to shared), but the
// call site here would look like this instead of the above:
// dynamic_cast<AnimVariantCallingMixin*>(function.engine())->callAnimationStateHandler(function, ..., handleResult);
// This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above
// (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast.
// gather results in (likely from an earlier update):
_animVars.copyVariantsFrom(value.results); // If multiple handlers write the same anim var, the last registgered wins. (_map preserves order).
}
}
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
if (_enableAnimGraph) {
@ -583,6 +649,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
return;
}
updateAnimationStateHandlers();
// evaluate the animation
AnimNode::Triggers triggersOut;
AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);

View file

@ -37,6 +37,8 @@
#define __hifi__Rig__
#include <QObject>
#include <QMutex>
#include <QScriptValue>
#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
@ -51,6 +53,12 @@ typedef std::shared_ptr<Rig> RigPointer;
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
public:
struct StateHandler {
AnimVariantMap results;
QStringList propertyNames;
QScriptValue function;
bool useNames;
};
struct HeadParameters {
float leanSideways = 0.0f; // degrees
@ -199,10 +207,14 @@ public:
AnimNode::ConstPointer getAnimNode() const { return _animNode; }
AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)
QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList);
void removeAnimationStateHandler(QScriptValue handler);
void animationStateHandlerResult(int identifier, QScriptValue result);
bool getModelOffset(glm::vec3& modelOffsetOut) const;
protected:
void updateAnimationStateHandlers();
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params);
@ -241,6 +253,11 @@ public:
float _desiredStateAge = 0.0f;
float _leftHandOverlayAlpha = 0.0f;
float _rightHandOverlayAlpha = 0.0f;
private:
QMap<int, StateHandler> _stateHandlers;
int _nextStateHandlerId {0};
QMutex _stateMutex;
};
#endif /* defined(__hifi__Rig__) */

View file

@ -59,39 +59,30 @@ Sound::Sound(const QUrl& url, bool isStereo) :
void Sound::downloadFinished(const QByteArray& data) {
// replace our byte array with the downloaded data
QByteArray rawAudioByteArray = QByteArray(data);
QString fileName = getURL().fileName();
const QString WAV_EXTENSION = ".wav";
QString fileName = getURL().fileName().toLower();
static const QString WAV_EXTENSION = ".wav";
static const QString RAW_EXTENSION = ".raw";
if (fileName.endsWith(WAV_EXTENSION)) {
QString headerContentType = "audio/x-wav";
//QByteArray headerContentType = reply->rawHeader("Content-Type");
QByteArray outputAudioByteArray;
// WAV audio file encountered
if (headerContentType == "audio/x-wav"
|| headerContentType == "audio/wav"
|| headerContentType == "audio/wave"
|| fileName.endsWith(WAV_EXTENSION)) {
QByteArray outputAudioByteArray;
interpretAsWav(rawAudioByteArray, outputAudioByteArray);
downSample(outputAudioByteArray);
} else {
// check if this was a stereo raw file
// since it's raw the only way for us to know that is if the file was called .stereo.raw
if (fileName.toLower().endsWith("stereo.raw")) {
_isStereo = true;
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
}
// Process as RAW file
downSample(rawAudioByteArray);
interpretAsWav(rawAudioByteArray, outputAudioByteArray);
downSample(outputAudioByteArray);
trimFrames();
} else if (fileName.endsWith(RAW_EXTENSION)) {
// check if this was a stereo raw file
// since it's raw the only way for us to know that is if the file was called .stereo.raw
if (fileName.toLower().endsWith("stereo.raw")) {
_isStereo = true;
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
}
// Process as RAW file
downSample(rawAudioByteArray);
trimFrames();
} else {
qCDebug(audio) << "Network reply without 'Content-Type'.";
qCDebug(audio) << "Unknown sound file type";
}
_isReady = true;

View file

@ -21,62 +21,43 @@
HandData::HandData(AvatarData* owningAvatar) :
_owningAvatarData(owningAvatar)
{
// Start with two palms
addNewPalm();
addNewPalm();
addNewPalm(LeftHand);
addNewPalm(RightHand);
}
glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {
return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale();
}
PalmData& HandData::addNewPalm() {
_palms.push_back(PalmData(this));
PalmData& HandData::addNewPalm(Hand whichHand) {
QWriteLocker locker(&_palmsLock);
_palms.push_back(PalmData(this, whichHand));
return _palms.back();
}
const PalmData* HandData::getPalm(int sixSenseID) const {
PalmData HandData::getCopyOfPalmData(Hand hand) const {
QReadLocker locker(&_palmsLock);
// the palms are not necessarily added in left-right order,
// so we have to search for the right SixSenseID
for (unsigned int i = 0; i < _palms.size(); i++) {
const PalmData* palm = &(_palms[i]);
if (palm->getSixenseID() == sixSenseID) {
return palm->isActive() ? palm : NULL;
// so we have to search for the correct hand
for (const auto& palm : _palms) {
if (palm.whichHand() == hand && palm.isActive()) {
return palm;
}
}
return NULL;
return PalmData(); // invalid hand
}
void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const {
leftPalmIndex = -1;
rightPalmIndex = -1;
for (size_t i = 0; i < _palms.size(); i++) {
const PalmData& palm = _palms[i];
if (palm.isActive()) {
if (palm.getSixenseID() == LEFT_HAND_INDEX) {
leftPalmIndex = i;
}
if (palm.getSixenseID() == RIGHT_HAND_INDEX) {
rightPalmIndex = i;
}
}
}
}
PalmData::PalmData(HandData* owningHandData) :
PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) :
_rawRotation(0.0f, 0.0f, 0.0f, 1.0f),
_rawPosition(0.0f),
_rawVelocity(0.0f),
_rawAngularVelocity(0.0f),
_totalPenetration(0.0f),
_controllerButtons(0),
_isActive(false),
_sixenseID(SIXENSEID_INVALID),
_numFramesWithoutData(0),
_owningHandData(owningHandData),
_isCollidingWithVoxel(false),
_isCollidingWithPalm(false),
_collisionlessPaddleExpiry(0) {
_hand(hand) {
}
void PalmData::addToPosition(const glm::vec3& delta) {
@ -85,9 +66,9 @@ void PalmData::addToPosition(const glm::vec3& delta) {
bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
const PalmData*& collidingPalm) const {
for (size_t i = 0; i < _palms.size(); ++i) {
const PalmData& palm = _palms[i];
QReadLocker locker(&_palmsLock);
for (const auto& palm : _palms) {
if (!palm.isActive()) {
continue;
}

View file

@ -12,26 +12,30 @@
#ifndef hifi_HandData_h
#define hifi_HandData_h
#include <functional>
#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QReadWriteLock>
#include <NumericalConstants.h>
#include <SharedUtil.h>
class AvatarData;
class PalmData;
const int LEFT_HAND_INDEX = 0;
const int RIGHT_HAND_INDEX = 1;
const int NUM_HANDS = 2;
const int SIXENSEID_INVALID = -1;
class HandData {
public:
enum Hand {
LeftHand,
RightHand,
UnknownHand,
NUMBER_OF_HANDS
};
HandData(AvatarData* owningAvatar);
virtual ~HandData() {}
@ -46,15 +50,9 @@ public:
glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;
std::vector<PalmData>& getPalms() { return _palms; }
const std::vector<PalmData>& getPalms() const { return _palms; }
const PalmData* getPalm(int sixSenseID) const;
size_t getNumPalms() const { return _palms.size(); }
PalmData& addNewPalm();
PalmData getCopyOfPalmData(Hand hand) const;
/// Finds the indices of the left and right palms according to their locations, or -1 if either or
/// both is not found.
void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const;
std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; }
/// Checks for penetration between the described sphere and the hand.
/// \param penetratorCenter the center of the penetration test sphere
@ -67,14 +65,22 @@ public:
glm::quat getBaseOrientation() const;
/// Allows a lamda function write access to the specific palm for this Hand, this might
/// modify the _palms vector
template<typename PalmModifierFunction> void modifyPalm(Hand whichHand, PalmModifierFunction callback);
friend class AvatarData;
protected:
AvatarData* _owningAvatarData;
std::vector<PalmData> _palms;
mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive };
glm::vec3 getBasePosition() const;
float getBaseScale() const;
PalmData& addNewPalm(Hand whichHand);
PalmData& getPalmData(Hand hand);
private:
// privatize copy ctor and assignment operator so copies of this object cannot be made
HandData(const HandData&);
@ -84,25 +90,30 @@ private:
class PalmData {
public:
PalmData(HandData* owningHandData);
PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand);
glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }
glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }
const glm::vec3& getRawPosition() const { return _rawPosition; }
bool isActive() const { return _isActive; }
int getSixenseID() const { return _sixenseID; }
bool isValid() const { return _owningHandData; }
void setActive(bool active) { _isActive = active; }
void setSixenseID(int id) { _sixenseID = id; }
void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; };
HandData::Hand whichHand() const { return _hand; }
void setHand(HandData::Hand hand) { _hand = hand; }
void setRawRotation(const glm::quat& rawRotation) { _rawRotation = rawRotation; };
glm::quat getRawRotation() const { return _rawRotation; }
glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); }
void addToPosition(const glm::vec3& delta);
void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
@ -120,31 +131,11 @@ public:
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }
// Controller buttons
void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; }
void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; }
unsigned int getControllerButtons() const { return _controllerButtons; }
unsigned int getLastControllerButtons() const { return _lastControllerButtons; }
// FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information
// from an action and/or the UserInputMapper instead of piping it through here.
void setTrigger(float trigger) { _trigger = trigger; }
float getTrigger() const { return _trigger; }
void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; }
float getJoystickX() const { return _joystickX; }
float getJoystickY() const { return _joystickY; }
bool getIsCollidingWithVoxel() const { return _isCollidingWithVoxel; }
void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; }
bool getIsCollidingWithPalm() const { return _isCollidingWithPalm; }
void setIsCollidingWithPalm(bool isCollidingWithPalm) { _isCollidingWithPalm = isCollidingWithPalm; }
bool hasPaddle() const { return _collisionlessPaddleExpiry < usecTimestampNow(); }
void updateCollisionlessPaddleExpiry() { _collisionlessPaddleExpiry = usecTimestampNow() + USECS_PER_SECOND; }
/// Store position where the palm holds the ball.
void getBallHoldPosition(glm::vec3& position) const;
// return world-frame:
glm::vec3 getFingerDirection() const;
glm::vec3 getNormal() const;
@ -155,25 +146,29 @@ private:
glm::vec3 _rawPosition;
glm::vec3 _rawVelocity;
glm::vec3 _rawAngularVelocity;
glm::quat _rawDeltaRotation;
glm::quat _lastRotation;
glm::vec3 _tipPosition;
glm::vec3 _tipVelocity;
glm::vec3 _totalPenetration; // accumulator for per-frame penetrations
glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations
unsigned int _controllerButtons;
unsigned int _lastControllerButtons;
float _trigger;
float _joystickX, _joystickY;
bool _isActive; // This has current valid data
int _sixenseID; // Sixense controller ID for this palm
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
bool _isActive; /// This has current valid data
int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost.
HandData* _owningHandData;
bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel
bool _isCollidingWithPalm;
quint64 _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding
HandData::Hand _hand;
};
template<typename PalmModifierFunction> void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) {
QReadLocker locker(&_palmsLock);
for (auto& palm : _palms) {
if (palm.whichHand() == whichHand && palm.isValid()) {
callback(palm);
return;
}
}
}
#endif // hifi_HandData_h

View file

@ -0,0 +1,14 @@
set(TARGET_NAME controllers)
# set a default root dir for each of our optional externals if it was not passed
setup_hifi_library(Script)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
link_hifi_libraries(shared)
GroupSources("src/controllers")
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})

View file

@ -0,0 +1,120 @@
//
// Created by Bradley Austin Davis on 2015/10/19
// 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 "Actions.h"
#include "impl/endpoints/ActionEndpoint.h"
namespace controller {
Input::NamedPair makePair(ChannelType type, Action action, const QString& name) {
auto input = Input(UserInputMapper::ACTIONS_DEVICE, toInt(action), type);
return Input::NamedPair(input, name);
}
Input::NamedPair makeAxisPair(Action action, const QString& name) {
return makePair(ChannelType::AXIS, action, name);
}
Input::NamedPair makeButtonPair(Action action, const QString& name) {
return makePair(ChannelType::BUTTON, action, name);
}
Input::NamedPair makePosePair(Action action, const QString& name) {
return makePair(ChannelType::POSE, action, name);
}
EndpointPointer ActionsDevice::createEndpoint(const Input& input) const {
return std::make_shared<ActionEndpoint>(input);
}
// Device functions
Input::NamedVector ActionsDevice::getAvailableInputs() const {
static Input::NamedVector availableInputs {
makeAxisPair(Action::TRANSLATE_X, "TranslateX"),
makeAxisPair(Action::TRANSLATE_Y, "TranslateY"),
makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"),
makeAxisPair(Action::ROLL, "Roll"),
makeAxisPair(Action::PITCH, "Pitch"),
makeAxisPair(Action::YAW, "Yaw"),
makeAxisPair(Action::STEP_YAW, "StepYaw"),
makeAxisPair(Action::STEP_PITCH, "StepPitch"),
makeAxisPair(Action::STEP_ROLL, "StepRoll"),
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"),
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"),
makePosePair(Action::LEFT_HAND, "LeftHand"),
makePosePair(Action::RIGHT_HAND, "RightHand"),
makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),
makeButtonPair(Action::SHIFT, "Shift"),
makeButtonPair(Action::ACTION1, "PrimaryAction"),
makeButtonPair(Action::ACTION2, "SecondaryAction"),
makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"),
makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
// Aliases and bisected versions
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"),
makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"),
makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"),
makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"),
makeAxisPair(Action::VERTICAL_DOWN, "Down"),
makeAxisPair(Action::VERTICAL_UP, "Up"),
makeAxisPair(Action::YAW_LEFT, "YawLeft"),
makeAxisPair(Action::YAW_RIGHT, "YawRight"),
makeAxisPair(Action::PITCH_DOWN, "PitchDown"),
makeAxisPair(Action::PITCH_UP, "PitchUp"),
makeAxisPair(Action::BOOM_IN, "BoomIn"),
makeAxisPair(Action::BOOM_OUT, "BoomOut"),
// Deprecated aliases
// FIXME remove after we port all scripts
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"),
makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"),
makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"),
makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"),
makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"),
makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"),
makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"),
makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"),
makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"),
makeAxisPair(Action::PITCH_UP, "PITCH_UP"),
makeAxisPair(Action::BOOM_IN, "BOOM_IN"),
makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"),
makePosePair(Action::LEFT_HAND, "LEFT_HAND"),
makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"),
makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"),
makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"),
makeButtonPair(Action::SHIFT, "SHIFT"),
makeButtonPair(Action::ACTION1, "ACTION1"),
makeButtonPair(Action::ACTION2, "ACTION2"),
makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"),
makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"),
};
return availableInputs;
}
void ActionsDevice::update(float deltaTime, bool jointsCaptured) {
}
void ActionsDevice::focusOutEvent() {
}
ActionsDevice::ActionsDevice() : InputDevice("Actions") {
_deviceID = UserInputMapper::ACTIONS_DEVICE;
}
ActionsDevice::~ActionsDevice() {}
}

View file

@ -0,0 +1,103 @@
//
// Created by Bradley Austin Davis on 2015/10/19
// 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
//
#pragma once
#ifndef hifi_controller_Actions_h
#define hifi_controller_Actions_h
#include <QtCore/QObject>
#include <QtCore/QVector>
#include "InputDevice.h"
namespace controller {
// Actions are the output channels of the Mapper, that's what the InputChannel map to
// For now the Actions are hardcoded, this is bad, but we will fix that in the near future
enum class Action {
TRANSLATE_X = 0,
TRANSLATE_Y,
TRANSLATE_Z,
ROTATE_X, PITCH = ROTATE_X,
ROTATE_Y, YAW = ROTATE_Y,
ROTATE_Z, ROLL = ROTATE_Z,
STEP_YAW,
// FIXME does this have a use case?
STEP_PITCH,
// FIXME does this have a use case?
STEP_ROLL,
STEP_TRANSLATE_X,
STEP_TRANSLATE_Y,
STEP_TRANSLATE_Z,
TRANSLATE_CAMERA_Z,
NUM_COMBINED_AXES,
LEFT_HAND = NUM_COMBINED_AXES,
RIGHT_HAND,
LEFT_HAND_CLICK,
RIGHT_HAND_CLICK,
ACTION1,
ACTION2,
CONTEXT_MENU,
TOGGLE_MUTE,
SHIFT,
// Biseced aliases for TRANSLATE_Z
LONGITUDINAL_BACKWARD,
LONGITUDINAL_FORWARD,
// Biseced aliases for TRANSLATE_X
LATERAL_LEFT,
LATERAL_RIGHT,
// Biseced aliases for TRANSLATE_Y
VERTICAL_DOWN,
VERTICAL_UP,
// Biseced aliases for ROTATE_Y
YAW_LEFT,
YAW_RIGHT,
// Biseced aliases for ROTATE_X
PITCH_DOWN,
PITCH_UP,
// Biseced aliases for TRANSLATE_CAMERA_Z
BOOM_IN,
BOOM_OUT,
NUM_ACTIONS,
};
template <typename T>
int toInt(T enumValue) { return static_cast<int>(enumValue); }
class ActionsDevice : public QObject, public InputDevice {
Q_OBJECT
Q_PROPERTY(QString name READ getName)
public:
virtual EndpointPointer createEndpoint(const Input& input) const override;
virtual Input::NamedVector getAvailableInputs() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
ActionsDevice();
virtual ~ActionsDevice();
};
}
#endif // hifi_StandardController_h

View file

@ -0,0 +1,14 @@
//
// Created by Bradley Austin Davis on 2015/10/18
// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15)
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DeviceProxy.h"
namespace controller {
}

View file

@ -0,0 +1,47 @@
//
// Created by Bradley Austin Davis on 2015/10/18
// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15)
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_controllers_DeviceProxy_h
#define hifi_controllers_DeviceProxy_h
#include <functional>
#include <QtCore/QString>
#include <QtCore/QVector>
#include "Input.h"
#include "Pose.h"
namespace controller {
/*
using Modifiers = std::vector<Input>;
typedef QPair<Input, QString> InputPair;
class Endpoint;
using EndpointPtr = std::shared_ptr<Endpoint>;
template<typename T>
using InputGetter = std::function<T(const Input& input, int timestamp)>;
using ButtonGetter = InputGetter<bool>;
using AxisGetter = InputGetter<float>;
using PoseGetter = InputGetter<Pose>;
using ResetBindings = std::function<bool()>;
using AvailableInputGetter = std::function<Input::NamedVector()>;
using EndpointCreator = std::function<EndpointPtr(const Input&)>;
class DeviceProxy {
public:
using Pointer = std::shared_ptr<DeviceProxy>;
QString _name;
};
*/
}
#endif

View file

@ -0,0 +1,37 @@
//
// Created by Bradley Austin Davis 2015/10/20
// 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
//
#pragma once
#ifndef hifi_Controllers_Forward_h
#define hifi_Controllers_Forward_h
namespace controller {
class Endpoint;
using EndpointPointer = std::shared_ptr<Endpoint>;
using EndpointList = std::list<EndpointPointer>;
class Filter;
using FilterPointer = std::shared_ptr<Filter>;
using FilterList = std::list<FilterPointer>;
class Route;
using RoutePointer = std::shared_ptr<Route>;
using RouteList = std::list<RoutePointer>;
class Conditional;
using ConditionalPointer = std::shared_ptr<Conditional>;
using ConditionalList = std::list<ConditionalPointer>;
class Mapping;
using MappingPointer = std::shared_ptr<Mapping>;
using MappingList = std::list<MappingPointer>;
}
#endif

View file

@ -0,0 +1,17 @@
//
// Created by Bradley Austin Davis on 2015/10/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 "Input.h"
namespace controller {
const Input Input::INVALID_INPUT = Input(0x7fffffff);
const uint16_t Input::INVALID_DEVICE = Input::INVALID_INPUT.device;
const uint16_t Input::INVALID_CHANNEL = Input::INVALID_INPUT.channel;
const uint16_t Input::INVALID_TYPE = Input::INVALID_INPUT.type;
}

View file

@ -0,0 +1,74 @@
//
// Created by Bradley Austin Davis on 2015/10/18
// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15)
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_controllers_Input_h
#define hifi_controllers_Input_h
#include <GLMHelpers.h>
namespace controller {
enum class ChannelType {
UNKNOWN = 0,
BUTTON,
AXIS,
POSE,
RUMBLE,
INVALID = 0x7
};
// Input is the unique identifier to find a n input channel of a particular device
// Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped
// to the Action channels
struct Input {
union {
uint32_t id{ 0 }; // by default Input is 0 meaning invalid
struct {
uint16_t device; // Up to 64K possible devices
uint16_t channel : 12 ; // 2^12 possible channel per Device
uint16_t type : 3; // 2 bits to store the Type directly in the ID
uint16_t padding : 1; // 2 bits to store the Type directly in the ID
};
};
bool isValid() const { return (id != INVALID_INPUT.id); }
uint16_t getDevice() const { return device; }
uint16_t getChannel() const { return channel; }
uint32_t getID() const { return id; }
ChannelType getType() const { return (ChannelType) type; }
bool isButton() const { return getType() == ChannelType::BUTTON; }
bool isAxis() const { return getType() == ChannelType::AXIS; }
bool isPose() const { return getType() == ChannelType::POSE; }
// WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2)
// where the default initializer (a C++-11ism) for the union data above is not applied.
explicit Input() {}
explicit Input(uint32_t id) : id(id) {}
explicit Input(uint16_t device, uint16_t channel, ChannelType type) : device(device), channel(channel), type(uint16_t(type)), padding(0) {}
Input(const Input& src) : id(src.id) {}
Input& operator = (const Input& src) { id = src.id; return (*this); }
bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; }
bool operator !=(const Input& right) const { return !(*this == right); }
bool operator < (const Input& src) const { return id < src.id; }
static const Input INVALID_INPUT;
static const uint16_t INVALID_DEVICE;
static const uint16_t INVALID_CHANNEL;
static const uint16_t INVALID_TYPE;
using NamedPair = QPair<Input, QString>;
using NamedVector = QVector<NamedPair>;
};
}
#endif

View file

@ -0,0 +1,114 @@
//
// InputDevice.cpp
// input-plugins/src/input-plugins
//
// Created by Sam Gondelman on 7/15/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 "InputDevice.h"
#include "Input.h"
#include "impl/endpoints/InputEndpoint.h"
namespace controller {
bool InputDevice::_lowVelocityFilter = false;
const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
//Constants for getCursorPixelRangeMultiplier()
const float MIN_PIXEL_RANGE_MULT = 0.4f;
const float MAX_PIXEL_RANGE_MULT = 2.0f;
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
//Returns a multiplier to be applied to the cursor range for the controllers
float InputDevice::getCursorPixelRangeMult() {
//scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
}
float InputDevice::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
} else {
return 0.0f;
}
}
return 0.0f;
}
float InputDevice::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
} else {
return 0.0f;
}
}
Pose InputDevice::getPose(int channel) const {
auto pose = _poseStateMap.find(channel);
if (pose != _poseStateMap.end()) {
return (*pose).second;
} else {
return Pose();
}
}
Input InputDevice::makeInput(controller::StandardButtonChannel button) const {
return Input(_deviceID, button, ChannelType::BUTTON);
}
Input InputDevice::makeInput(controller::StandardAxisChannel axis) const {
return Input(_deviceID, axis, ChannelType::AXIS);
}
Input InputDevice::makeInput(controller::StandardPoseChannel pose) const {
return Input(_deviceID, pose, ChannelType::POSE);
}
Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) const {
return Input::NamedPair(makeInput(button), name);
}
Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) const {
return Input::NamedPair(makeInput(axis), name);
}
Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) const {
return Input::NamedPair(makeInput(pose), name);
}
float InputDevice::getValue(ChannelType channelType, uint16_t channel) const {
switch (channelType) {
case ChannelType::AXIS:
return getAxis(channel);
case ChannelType::BUTTON:
return getButton(channel);
case ChannelType::POSE:
return getPose(channel).valid ? 1.0f : 0.0f;
default:
break;
}
return 0.0f;
}
float InputDevice::getValue(const Input& input) const {
return getValue(input.getType(), input.channel);
}
EndpointPointer InputDevice::createEndpoint(const Input& input) const {
return std::make_shared<InputEndpoint>(input);
}
}

View file

@ -10,12 +10,27 @@
//
#pragma once
#include "UserInputMapper.h"
#include <memory>
#include <map>
#include <unordered_set>
#include <QtCore/QString>
#include "Pose.h"
#include "Input.h"
#include "StandardControls.h"
#include "DeviceProxy.h"
// Event types for each controller
const unsigned int CONTROLLER_0_EVENT = 1500U;
const unsigned int CONTROLLER_1_EVENT = 1501U;
namespace controller {
class Endpoint;
using EndpointPointer = std::shared_ptr<Endpoint>;
// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
// e.g. class Example : public InputPlugin, public InputDevice
// instead of class Example : public InputDevice, public InputPlugin
@ -23,17 +38,22 @@ class InputDevice {
public:
InputDevice(const QString& name) : _name(name) {}
using Pointer = std::shared_ptr<InputDevice>;
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;
typedef std::map<int, Pose> PoseStateMap;
// Get current state for each channel
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::PoseValue getPose(int channel) const;
Pose getPose(int channel) const;
virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0;
float getValue(const Input& input) const;
float getValue(ChannelType channelType, uint16_t channel) const;
Pose getPoseValue(uint16_t channel) const;
const QString& getName() const { return _name; }
// Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas
@ -42,20 +62,33 @@ public:
virtual void focusOutEvent() = 0;
int getDeviceID() { return _deviceID; }
void setDeviceID(int deviceID) { _deviceID = deviceID; }
static float getCursorPixelRangeMult();
static float getReticleMoveSpeed() { return reticleMoveSpeed; }
static void setReticleMoveSpeed(float sixenseReticleMoveSpeed) { reticleMoveSpeed = sixenseReticleMoveSpeed; }
static float getReticleMoveSpeed() { return _reticleMoveSpeed; }
static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; }
static bool getLowVelocityFilter() { return _lowVelocityFilter; };
Input makeInput(StandardButtonChannel button) const;
Input makeInput(StandardAxisChannel axis) const;
Input makeInput(StandardPoseChannel pose) const;
Input::NamedPair makePair(StandardButtonChannel button, const QString& name) const;
Input::NamedPair makePair(StandardAxisChannel button, const QString& name) const;
Input::NamedPair makePair(StandardPoseChannel button, const QString& name) const;
public slots:
static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };
protected:
int _deviceID = 0;
friend class UserInputMapper;
QString _name;
virtual Input::NamedVector getAvailableInputs() const = 0;
virtual QString getDefaultMappingConfig() const { return QString(); }
virtual EndpointPointer createEndpoint(const Input& input) const;
uint16_t _deviceID { Input::INVALID_DEVICE };
const QString _name;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
@ -64,5 +97,7 @@ protected:
static bool _lowVelocityFilter;
private:
static float reticleMoveSpeed;
};
static float _reticleMoveSpeed;
};
}

View file

@ -0,0 +1,11 @@
//
// Created by Bradley Austin Davis 2015/10/11
// 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 "Logging.h"
Q_LOGGING_CATEGORY(controllers, "hifi.controllers")

View file

@ -0,0 +1,16 @@
//
// Created by Bradley Austin Davis 2015/10/11
// 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_Controllers_Logging_h
#define hifi_Controllers_Logging_h
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(controllers)
#endif

View file

@ -0,0 +1,49 @@
//
// Created by Bradley Austin Davis on 2015/10/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 <QtScript/QScriptEngine>
#include <QtScript/QScriptValue>
#include <RegisteredMetaTypes.h>
#include "Pose.h"
namespace controller {
Pose::Pose(const vec3& translation, const quat& rotation,
const vec3& velocity, const quat& angularVelocity) :
translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { }
bool Pose::operator==(const Pose& right) const {
// invalid poses return false for comparison, even against identical invalid poses, like NaN
if (!valid || !right.valid) {
return false;
}
// FIXME add margin of error? Or add an additional withinEpsilon function?
return translation == right.getTranslation() && rotation == right.getRotation() &&
velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity();
}
QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) {
QScriptValue obj = engine->newObject();
obj.setProperty("translation", vec3toScriptValue(engine, pose.translation));
obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation));
obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity));
obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity));
obj.setProperty("valid", pose.valid);
return obj;
}
void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) {
// nothing for now...
}
}

View file

@ -0,0 +1,50 @@
//
// Created by Bradley Austin Davis on 2015/10/18
// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15)
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_controllers_Pose_h
#define hifi_controllers_Pose_h
class QScriptEngine;
class QScriptValue;
#include <GLMHelpers.h>
namespace controller {
struct Pose {
public:
vec3 translation;
quat rotation;
vec3 velocity;
quat angularVelocity;
bool valid{ false };
Pose() {}
Pose(const vec3& translation, const quat& rotation,
const vec3& velocity = vec3(), const quat& angularVelocity = quat());
Pose(const Pose&) = default;
Pose& operator = (const Pose&) = default;
bool operator ==(const Pose& right) const;
bool operator !=(const Pose& right) const { return !(*this == right); }
bool isValid() const { return valid; }
vec3 getTranslation() const { return translation; }
quat getRotation() const { return rotation; }
vec3 getVelocity() const { return velocity; }
quat getAngularVelocity() const { return angularVelocity; }
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
static void fromScriptValue(const QScriptValue& object, Pose& event);
};
}
//Q_DECLARE_METATYPE(controller::Pose);
#endif

View file

@ -0,0 +1,241 @@
//
// Created by Bradley Austin Davis 2015/10/09
// 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 "ScriptingInterface.h"
#include <mutex>
#include <set>
#include <QtCore/QRegularExpression>
#include <QEventLoop>
#include <QThread>
#include <GLMHelpers.h>
#include <DependencyManager.h>
#include <ResourceManager.h>
#include "impl/MappingBuilderProxy.h"
#include "Logging.h"
#include "InputDevice.h"
static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" };
static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
QVariantMap deviceMap;
for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) {
const auto& input = inputMapping.first;
const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION);
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
<< QString::number(input.getID(), 16) << ": " << inputName;
deviceMap.insert(inputName, input.getID());
}
return deviceMap;
}
// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block
controller::ScriptingInterface::ScriptingInterface() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connect(userInputMapper.data(), &UserInputMapper::actionEvent, this, &controller::ScriptingInterface::actionEvent);
connect(userInputMapper.data(), &UserInputMapper::inputEvent, this, &controller::ScriptingInterface::inputEvent);
// FIXME make this thread safe
connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, this, [=] {
updateMaps();
emit hardwareChanged();
});
qCDebug(controllers) << "Setting up standard controller abstraction";
_standard = createDeviceMap(userInputMapper->getStandardDevice());
// FIXME allow custom user actions?
auto actionNames = userInputMapper->getActionNames();
qCDebug(controllers) << "Setting up standard actions";
for (const auto& namedInput : userInputMapper->getActionInputs()) {
const QString& actionName = namedInput.second;
const Input& actionInput = namedInput.first;
qCDebug(controllers) << "\tAction: " << actionName << " " << actionInput.getChannel();
// Expose the IDs to JS
QString cleanActionName = QString(actionName).remove(SANITIZE_NAME_EXPRESSION);
_actions.insert(cleanActionName, actionInput.getID());
}
updateMaps();
}
namespace controller {
QObject* ScriptingInterface::newMapping(const QString& mappingName) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return new MappingBuilderProxy(*userInputMapper, userInputMapper->newMapping(mappingName));
}
void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->enableMapping(mappingName, enable);
}
float ScriptingInterface::getValue(const int& source) const {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getValue(Input((uint32_t)source));
}
float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const {
return getValue(Input(device, source, ChannelType::BUTTON).getID());
}
float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const {
return getValue(Input(device, source, ChannelType::AXIS).getID());
}
Pose ScriptingInterface::getPoseValue(const int& source) const {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getPose(Input((uint32_t)source));
}
Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const {
return getPoseValue(Input(device, source, ChannelType::POSE).getID());
}
//bool ScriptingInterface::isPrimaryButtonPressed() const {
// return isButtonPressed(StandardButtonChannel::A);
//}
//
//glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const {
// return getJoystickPosition(0);
//}
//int ScriptingInterface::getNumberOfButtons() const {
// return StandardButtonChannel::NUM_STANDARD_BUTTONS;
//}
//bool ScriptingInterface::isButtonPressed(int buttonIndex) const {
// return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true;
//}
//int ScriptingInterface::getNumberOfTriggers() const {
// return StandardCounts::TRIGGERS;
//}
//float ScriptingInterface::getTriggerValue(int triggerIndex) const {
// return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT);
//}
//int ScriptingInterface::getNumberOfJoysticks() const {
// return StandardCounts::ANALOG_STICKS;
//}
//glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const {
// StandardAxisChannel xid = StandardAxisChannel::LX;
// StandardAxisChannel yid = StandardAxisChannel::LY;
// if (joystickIndex != 0) {
// xid = StandardAxisChannel::RX;
// yid = StandardAxisChannel::RY;
// }
// vec2 result;
// result.x = getAxisValue(xid);
// result.y = getAxisValue(yid);
// return result;
//}
//int ScriptingInterface::getNumberOfSpatialControls() const {
// return StandardCounts::POSES;
//}
//glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const {
// // FIXME extract the position from the standard pose
// return vec3();
//}
//glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
// // FIXME extract the velocity from the standard pose
// return vec3();
//}
//glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const {
// // FIXME extract the normal from the standard pose
// return vec3();
//}
//
//glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
// // FIXME extract the rotation from the standard pose
// return quat();
//}
QVector<Action> ScriptingInterface::getAllActions() {
return DependencyManager::get<UserInputMapper>()->getAllActions();
}
QString ScriptingInterface::getDeviceName(unsigned int device) {
return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device);
}
QVector<Input::NamedPair> ScriptingInterface::getAvailableInputs(unsigned int device) {
return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device);
}
int ScriptingInterface::findDevice(QString name) {
return DependencyManager::get<UserInputMapper>()->findDevice(name);
}
QVector<QString> ScriptingInterface::getDeviceNames() {
return DependencyManager::get<UserInputMapper>()->getDeviceNames();
}
float ScriptingInterface::getActionValue(int action) {
return DependencyManager::get<UserInputMapper>()->getActionState(Action(action));
}
int ScriptingInterface::findAction(QString actionName) {
return DependencyManager::get<UserInputMapper>()->findAction(actionName);
}
QVector<QString> ScriptingInterface::getActionNames() const {
return DependencyManager::get<UserInputMapper>()->getActionNames();
}
void ScriptingInterface::updateMaps() {
QVariantMap newHardware;
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
auto devices = userInputMapper->getDevices();
for (const auto& deviceMapping : devices) {
auto deviceID = deviceMapping.first;
if (deviceID != userInputMapper->getStandardDeviceID()) {
auto device = deviceMapping.second;
auto deviceName = QString(device->getName()).remove(SANITIZE_NAME_EXPRESSION);
qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName;
if (newHardware.contains(deviceName)) {
continue;
}
// Expose the IDs to JS
newHardware.insert(deviceName, createDeviceMap(device));
}
}
_hardware = newHardware;
}
QObject* ScriptingInterface::parseMapping(const QString& json) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto mapping = userInputMapper->parseMapping(json);
return new MappingBuilderProxy(*userInputMapper, mapping);
}
QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
return nullptr;
}
} // namespace controllers

View file

@ -0,0 +1,155 @@
//
// AbstractControllerScriptingInterface.h
// libraries/script-engine/src
//
// Created by Brad Hefta-Gaub on 12/17/13.
// Copyright 2013 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
//
#pragma once
#ifndef hifi_AbstractControllerScriptingInterface_h
#define hifi_AbstractControllerScriptingInterface_h
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QThread>
#include <QtCore/QObject>
#include <QtCore/QVariant>
#include <QtQml/QJSValue>
#include <QtScript/QScriptValue>
#include <DependencyManager.h>
#include "UserInputMapper.h"
#include "StandardControls.h"
namespace controller {
class InputController : public QObject {
Q_OBJECT
public:
using Key = unsigned int;
using Pointer = std::shared_ptr<InputController>;
virtual void update() = 0;
virtual Key getKey() const = 0;
public slots:
virtual bool isActive() const = 0;
virtual glm::vec3 getAbsTranslation() const = 0;
virtual glm::quat getAbsRotation() const = 0;
virtual glm::vec3 getLocTranslation() const = 0;
virtual glm::quat getLocRotation() const = 0;
signals:
//void spatialEvent(const SpatialEvent& event);
};
/// handles scripting of input controller commands from JS
class ScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL)
Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL)
Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL)
public:
ScriptingInterface();
virtual ~ScriptingInterface() {};
Q_INVOKABLE QVector<Action> getAllActions();
Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device);
Q_INVOKABLE QString getDeviceName(unsigned int device);
Q_INVOKABLE float getActionValue(int action);
Q_INVOKABLE int findDevice(QString name);
Q_INVOKABLE QVector<QString> getDeviceNames();
Q_INVOKABLE int findAction(QString actionName);
Q_INVOKABLE QVector<QString> getActionNames() const;
Q_INVOKABLE float getValue(const int& source) const;
Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;
Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const;
Q_INVOKABLE Pose getPoseValue(const int& source) const;
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }
Q_INVOKABLE QObject* parseMapping(const QString& json);
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
//Q_INVOKABLE bool isPrimaryButtonPressed() const;
//Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
//Q_INVOKABLE int getNumberOfButtons() const;
//Q_INVOKABLE bool isButtonPressed(int buttonIndex) const;
//Q_INVOKABLE int getNumberOfTriggers() const;
//Q_INVOKABLE float getTriggerValue(int triggerIndex) const;
//Q_INVOKABLE int getNumberOfJoysticks() const;
//Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const;
//Q_INVOKABLE int getNumberOfSpatialControls() const;
//Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const;
//Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const;
//Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const;
//Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const;
Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; }
Q_INVOKABLE const QVariantMap& getActions() { return _actions; }
Q_INVOKABLE const QVariantMap& getStandard() { return _standard; }
bool isMouseCaptured() const { return _mouseCaptured; }
bool isTouchCaptured() const { return _touchCaptured; }
bool isWheelCaptured() const { return _wheelCaptured; }
bool areActionsCaptured() const { return _actionsCaptured; }
public slots:
virtual void captureMouseEvents() { _mouseCaptured = true; }
virtual void releaseMouseEvents() { _mouseCaptured = false; }
virtual void captureTouchEvents() { _touchCaptured = true; }
virtual void releaseTouchEvents() { _touchCaptured = false; }
virtual void captureWheelEvents() { _wheelCaptured = true; }
virtual void releaseWheelEvents() { _wheelCaptured = false; }
virtual void captureActionEvents() { _actionsCaptured = true; }
virtual void releaseActionEvents() { _actionsCaptured = false; }
signals:
void actionEvent(int action, float state);
void inputEvent(int action, float state);
void hardwareChanged();
private:
// Update the exposed variant maps reporting active hardware
void updateMaps();
QVariantMap _hardware;
QVariantMap _actions;
QVariantMap _standard;
bool _mouseCaptured{ false };
bool _touchCaptured{ false };
bool _wheelCaptured{ false };
bool _actionsCaptured{ false };
};
}
#endif // hifi_AbstractControllerScriptingInterface_h

View file

@ -0,0 +1,158 @@
//
// StandardController.cpp
// input-plugins/src/input-plugins
//
// Created by Brad Hefta-Gaub on 2015-10-11.
// 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 "StandardController.h"
#include <PathUtils.h>
#include "UserInputMapper.h"
#include "impl/endpoints/StandardEndpoint.h"
namespace controller {
StandardController::StandardController() : InputDevice("Standard") {
_deviceID = UserInputMapper::STANDARD_DEVICE;
}
StandardController::~StandardController() {
}
void StandardController::update(float deltaTime, bool jointsCaptured) {
}
void StandardController::focusOutEvent() {
_axisStateMap.clear();
_buttonPressedMap.clear();
};
Input::NamedVector StandardController::getAvailableInputs() const {
static Input::NamedVector availableInputs {
// Buttons
makePair(A, "A"),
makePair(B, "B"),
makePair(X, "X"),
makePair(Y, "Y"),
// DPad
makePair(DU, "DU"),
makePair(DD, "DD"),
makePair(DL, "DL"),
makePair(DR, "DR"),
// Bumpers
makePair(LB, "LB"),
makePair(RB, "RB"),
// Stick press
makePair(LS, "LS"),
makePair(RS, "RS"),
// Center buttons
makePair(START, "Start"),
makePair(BACK, "Back"),
// Analog sticks
makePair(LY, "LY"),
makePair(LX, "LX"),
makePair(RY, "RY"),
makePair(RX, "RX"),
// Triggers
makePair(LT, "LT"),
makePair(RT, "RT"),
// Finger abstractions
makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"),
makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"),
makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"),
makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"),
makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"),
makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"),
makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"),
makePair(LEFT_GRIP, "LeftGrip"),
makePair(RIGHT_GRIP, "RightGrip"),
// Poses
makePair(LEFT_HAND, "LeftHand"),
makePair(RIGHT_HAND, "RightHand"),
// Aliases, PlayStation style names
makePair(LB, "L1"),
makePair(RB, "R1"),
makePair(LT, "L2"),
makePair(RT, "R2"),
makePair(LS, "L3"),
makePair(RS, "R3"),
makePair(BACK, "Select"),
makePair(A, "Cross"),
makePair(B, "Circle"),
makePair(X, "Square"),
makePair(Y, "Triangle"),
makePair(DU, "Up"),
makePair(DD, "Down"),
makePair(DL, "Left"),
makePair(DR, "Right"),
};
return availableInputs;
}
EndpointPointer StandardController::createEndpoint(const Input& input) const {
return std::make_shared<StandardEndpoint>(input);
}
QString StandardController::getDefaultMappingConfig() const {
static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json";
return DEFAULT_MAPPING_JSON;
}
// FIXME figure out how to move the shifted version to JSON
//void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
// const float DPAD_MOVE_SPEED = 0.5f;
// const float JOYSTICK_YAW_SPEED = 0.5f;
// const float JOYSTICK_PITCH_SPEED = 0.25f;
// const float BOOM_SPEED = 0.1f;
//
// // Hold front right shoulder button for precision controls
// // Left Joystick: Movement, strafing
// mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
//
// // Right Joystick: Camera orientation
// mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f);
//
// // Dpad movement
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
//
// // Button controls
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
//
// // Zoom
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
//
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB));
//
// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B));
// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A));
//}
}

View file

@ -0,0 +1,40 @@
//
// StandardController.h
// input-plugins/src/input-plugins
//
// Created by Brad Hefta-Gaub on 2015-10-11.
// 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_StandardController_h
#define hifi_StandardController_h
#include <QtCore/QObject>
#include <QtCore/QVector>
#include "InputDevice.h"
#include "StandardControls.h"
namespace controller {
class StandardController : public QObject, public InputDevice {
Q_OBJECT
Q_PROPERTY(QString name READ getName)
public:
virtual EndpointPointer createEndpoint(const Input& input) const override;
virtual Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
StandardController();
virtual ~StandardController();
};
}
#endif // hifi_StandardController_h

View file

@ -0,0 +1,85 @@
//
// Created by Bradley Austin Davis 2015/10/09
// 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
//
#pragma once
namespace controller {
// Needs to match order and values of SDL_GameControllerButton
enum StandardButtonChannel {
// Button quad
A = 0,
B,
X,
Y,
// Center buttons
BACK,
GUIDE,
START,
// Stick press
LS,
RS,
// Bumper press
LB,
RB,
// DPad
DU,
DD,
DL,
DR,
// These don't map to SDL types
LEFT_PRIMARY_THUMB,
LEFT_SECONDARY_THUMB,
RIGHT_PRIMARY_THUMB,
RIGHT_SECONDARY_THUMB,
LEFT_PRIMARY_INDEX,
LEFT_SECONDARY_INDEX,
RIGHT_PRIMARY_INDEX,
RIGHT_SECONDARY_INDEX,
LEFT_GRIP,
RIGHT_GRIP,
NUM_STANDARD_BUTTONS
};
// Needs to match order and values of SDL_GameControllerAxis
enum StandardAxisChannel {
// Left Analog stick
LX = 0,
LY,
LZ,
// Right Analog stick
RX,
RY,
RZ,
// Triggers
LT,
RT,
NUM_STANDARD_AXES
};
// No correlation to SDL
enum StandardPoseChannel {
LEFT_HAND = 0,
RIGHT_HAND,
HEAD,
NUM_STANDARD_POSES
};
enum StandardCounts {
TRIGGERS = 2,
ANALOG_STICKS = 2,
POSES = 2, // FIXME 3? if we want to expose the head?
};
}

View file

@ -0,0 +1,50 @@
//
// StateController.cpp
// controllers/src/controllers
//
// Created by Sam Gateau on 2015-10-27.
// 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 "StateController.h"
#include <PathUtils.h>
#include "DeviceProxy.h"
#include "UserInputMapper.h"
#include "impl/Endpoint.h"
namespace controller {
StateController::StateController() : InputDevice("Application") {
}
StateController::~StateController() {
}
void StateController::update(float deltaTime, bool jointsCaptured) {}
void StateController::focusOutEvent() {}
void StateController::addInputVariant(QString name, ReadLambda lambda) {
_namedReadLambdas.push_back(NamedReadLambda(name, lambda));
}
Input::NamedVector StateController::getAvailableInputs() const {
Input::NamedVector availableInputs;
int i = 0;
for (auto& pair : _namedReadLambdas) {
availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first));
i++;
}
return availableInputs;
}
EndpointPointer StateController::createEndpoint(const Input& input) const {
return std::make_shared<LambdaEndpoint>(_namedReadLambdas[input.getChannel()].second);
}
}

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