mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 23:26:25 +02:00
merge from upstream
This commit is contained in:
commit
896ed3927c
199 changed files with 9152 additions and 4020 deletions
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
101
examples/controllers/controllerMappings.js
Normal file
101
examples/controllers/controllerMappings.js
Normal 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("-------------------------------------------------------------------");
|
||||
});
|
|
@ -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);
|
||||
|
|
102
examples/controllers/handPosesDebug.js
Normal file
102
examples/controllers/handPosesDebug.js
Normal 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);
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
26
examples/example/lineExample.js
Normal file
26
examples/example/lineExample.js
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
161
examples/libraries/line.js
Normal 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;
|
||||
};
|
|
@ -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) ];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -38,7 +38,7 @@ var mouseLook = (function () {
|
|||
keyboardID = 0;
|
||||
|
||||
function onKeyPressEvent(event) {
|
||||
if (event.text == 'M') {
|
||||
if (event.text == 'm') {
|
||||
active = !active;
|
||||
updateMapping();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
77
examples/tests/controllerInterfaceTest.js
Normal file
77
examples/tests/controllerInterfaceTest.js
Normal 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();
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
31
interface/resources/controllers/hydra.json
Normal file
31
interface/resources/controllers/hydra.json
Normal 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" }
|
||||
]
|
||||
}
|
86
interface/resources/controllers/keyboardMouse.json
Normal file
86
interface/resources/controllers/keyboardMouse.json
Normal 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" }
|
||||
]
|
||||
}
|
24
interface/resources/controllers/mapping-config.json
Normal file
24
interface/resources/controllers/mapping-config.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
36
interface/resources/controllers/mapping-test0.json
Normal file
36
interface/resources/controllers/mapping-test0.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
43
interface/resources/controllers/standard-old.json
Normal file
43
interface/resources/controllers/standard-old.json
Normal 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" }
|
||||
]
|
||||
}
|
36
interface/resources/controllers/standard.json
Normal file
36
interface/resources/controllers/standard.json
Normal 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" }
|
||||
]
|
||||
}
|
||||
|
||||
|
26
interface/resources/controllers/vive.json
Normal file
26
interface/resources/controllers/vive.json
Normal 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" }
|
||||
]
|
||||
}
|
29
interface/resources/controllers/xbox.json
Normal file
29
interface/resources/controllers/xbox.json
Normal 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" }
|
||||
]
|
||||
}
|
111
interface/resources/qml/ScrollingGraph.qml
Normal file
111
interface/resources/qml/ScrollingGraph.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
161
interface/resources/qml/TestControllers.qml
Normal file
161
interface/resources/qml/TestControllers.qml
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
45
interface/resources/qml/controller/AnalogButton.qml
Normal file
45
interface/resources/qml/controller/AnalogButton.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
55
interface/resources/qml/controller/AnalogStick.qml
Normal file
55
interface/resources/qml/controller/AnalogStick.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
34
interface/resources/qml/controller/Hydra.qml
Normal file
34
interface/resources/qml/controller/Hydra.qml
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
35
interface/resources/qml/controller/Standard.qml
Normal file
35
interface/resources/qml/controller/Standard.qml
Normal 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
|
||||
}
|
||||
}
|
45
interface/resources/qml/controller/ToggleButton.qml
Normal file
45
interface/resources/qml/controller/ToggleButton.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
104
interface/resources/qml/controller/Xbox.qml
Normal file
104
interface/resources/qml/controller/Xbox.qml
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
18
interface/resources/qml/controller/hydra/HydraButtons.qml
Normal file
18
interface/resources/qml/controller/hydra/HydraButtons.qml
Normal 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
|
||||
|
||||
}
|
||||
|
||||
|
91
interface/resources/qml/controller/hydra/HydraStick.qml
Normal file
91
interface/resources/qml/controller/hydra/HydraStick.qml
Normal 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'
|
||||
}
|
||||
}
|
BIN
interface/resources/qml/controller/hydra/hydra.png
Normal file
BIN
interface/resources/qml/controller/hydra/hydra.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
42
interface/resources/qml/controller/xbox/DPad.qml
Normal file
42
interface/resources/qml/controller/xbox/DPad.qml
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
|
21
interface/resources/qml/controller/xbox/LeftAnalogStick.qml
Normal file
21
interface/resources/qml/controller/xbox/LeftAnalogStick.qml
Normal 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 ]
|
||||
}
|
||||
}
|
||||
|
||||
|
21
interface/resources/qml/controller/xbox/RightAnalogStick.qml
Normal file
21
interface/resources/qml/controller/xbox/RightAnalogStick.qml
Normal 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 ]
|
||||
}
|
||||
}
|
||||
|
||||
|
46
interface/resources/qml/controller/xbox/XboxButtons.qml
Normal file
46
interface/resources/qml/controller/xbox/XboxButtons.qml
Normal 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 |
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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); }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
121
libraries/animation/src/AnimVariant.cpp
Normal file
121
libraries/animation/src/AnimVariant.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
14
libraries/controllers/CMakeLists.txt
Normal file
14
libraries/controllers/CMakeLists.txt
Normal 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})
|
||||
|
120
libraries/controllers/src/controllers/Actions.cpp
Normal file
120
libraries/controllers/src/controllers/Actions.cpp
Normal 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() {}
|
||||
|
||||
}
|
103
libraries/controllers/src/controllers/Actions.h
Normal file
103
libraries/controllers/src/controllers/Actions.h
Normal 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
|
14
libraries/controllers/src/controllers/DeviceProxy.cpp
Normal file
14
libraries/controllers/src/controllers/DeviceProxy.cpp
Normal 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 {
|
||||
}
|
||||
|
47
libraries/controllers/src/controllers/DeviceProxy.h
Normal file
47
libraries/controllers/src/controllers/DeviceProxy.h
Normal 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
|
37
libraries/controllers/src/controllers/Forward.h
Normal file
37
libraries/controllers/src/controllers/Forward.h
Normal 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
|
17
libraries/controllers/src/controllers/Input.cpp
Normal file
17
libraries/controllers/src/controllers/Input.cpp
Normal 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;
|
||||
}
|
||||
|
74
libraries/controllers/src/controllers/Input.h
Normal file
74
libraries/controllers/src/controllers/Input.h
Normal 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
|
114
libraries/controllers/src/controllers/InputDevice.cpp
Normal file
114
libraries/controllers/src/controllers/InputDevice.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
11
libraries/controllers/src/controllers/Logging.cpp
Normal file
11
libraries/controllers/src/controllers/Logging.cpp
Normal 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")
|
16
libraries/controllers/src/controllers/Logging.h
Normal file
16
libraries/controllers/src/controllers/Logging.h
Normal 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
|
49
libraries/controllers/src/controllers/Pose.cpp
Normal file
49
libraries/controllers/src/controllers/Pose.cpp
Normal 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...
|
||||
}
|
||||
|
||||
}
|
||||
|
50
libraries/controllers/src/controllers/Pose.h
Normal file
50
libraries/controllers/src/controllers/Pose.h
Normal 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
|
241
libraries/controllers/src/controllers/ScriptingInterface.cpp
Normal file
241
libraries/controllers/src/controllers/ScriptingInterface.cpp
Normal 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
|
||||
|
||||
|
155
libraries/controllers/src/controllers/ScriptingInterface.h
Normal file
155
libraries/controllers/src/controllers/ScriptingInterface.h
Normal 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
|
158
libraries/controllers/src/controllers/StandardController.cpp
Normal file
158
libraries/controllers/src/controllers/StandardController.cpp
Normal 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));
|
||||
//}
|
||||
|
||||
}
|
40
libraries/controllers/src/controllers/StandardController.h
Normal file
40
libraries/controllers/src/controllers/StandardController.h
Normal 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
|
85
libraries/controllers/src/controllers/StandardControls.h
Normal file
85
libraries/controllers/src/controllers/StandardControls.h
Normal 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?
|
||||
};
|
||||
}
|
50
libraries/controllers/src/controllers/StateController.cpp
Normal file
50
libraries/controllers/src/controllers/StateController.cpp
Normal 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
Loading…
Reference in a new issue