mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-10 16:18:39 +02:00
merge upstream/master into andrew/ragdoll
This commit is contained in:
commit
bc1276944c
23 changed files with 1320 additions and 100 deletions
|
@ -611,7 +611,10 @@ var modelUploader = (function () {
|
||||||
index += nameLength;
|
index += nameLength;
|
||||||
|
|
||||||
if (name === "content" && previousNodeFilename !== "") {
|
if (name === "content" && previousNodeFilename !== "") {
|
||||||
geometry.embedded.push(previousNodeFilename);
|
// Blender 2.71 exporter "embeds" external textures as empty binary blobs so ignore these
|
||||||
|
if (propertyListLength > 5) {
|
||||||
|
geometry.embedded.push(previousNodeFilename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === "relativefilename") {
|
if (name === "relativefilename") {
|
||||||
|
|
443
examples/frisbee.js
Normal file
443
examples/frisbee.js
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
//
|
||||||
|
// frisbee.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Thijs Wenker on 7/5/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Requirements: Razer Hydra's
|
||||||
|
//
|
||||||
|
// Fun game to throw frisbee's to eachother. Hold the trigger on any of the hydra's to create or catch a frisbee.
|
||||||
|
//
|
||||||
|
// Tip: use this together with the squeezeHands.js script to make it look nicer.
|
||||||
|
//
|
||||||
|
// 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("toolBars.js");
|
||||||
|
|
||||||
|
const LEFT_PALM = 0;
|
||||||
|
const LEFT_TIP = 1;
|
||||||
|
const LEFT_BUTTON_FWD = 5;
|
||||||
|
const LEFT_BUTTON_3 = 3;
|
||||||
|
|
||||||
|
const RIGHT_PALM = 2;
|
||||||
|
const RIGHT_TIP = 3;
|
||||||
|
const RIGHT_BUTTON_FWD = 11;
|
||||||
|
const RIGHT_BUTTON_3 = 9;
|
||||||
|
|
||||||
|
const FRISBEE_RADIUS = 0.08;
|
||||||
|
const GRAVITY_STRENGTH = 0.5;
|
||||||
|
|
||||||
|
const CATCH_RADIUS = 0.5;
|
||||||
|
const MIN_SIMULATION_SPEED = 0.15;
|
||||||
|
const THROWN_VELOCITY_SCALING = 1.5;
|
||||||
|
|
||||||
|
const SOUNDS_ENABLED = true;
|
||||||
|
const FRISBEE_BUTTON_URL = "http://test.thoys.nl/hifi/images/frisbee/frisbee_button_by_Judas.svg";
|
||||||
|
const FRISBEE_MODEL_SCALE = 275;
|
||||||
|
const FRISBEE_MENU = "Toys>Frisbee";
|
||||||
|
const FRISBEE_DESIGN_MENU = "Toys>Frisbee>Design";
|
||||||
|
const FRISBEE_ENABLED_SETTING = "Frisbee>Enabled";
|
||||||
|
const FRISBEE_CREATENEW_SETTING = "Frisbee>CreateNew";
|
||||||
|
const FRISBEE_DESIGN_SETTING = "Frisbee>Design";
|
||||||
|
const FRISBEE_FORCE_MOUSE_CONTROLS_SETTING = "Frisbee>ForceMouseControls";
|
||||||
|
|
||||||
|
//Add your own designs in FRISBEE_DESIGNS, be sure to put frisbee in the URL if you want others to be able to catch it without having a copy of your frisbee script.
|
||||||
|
const FRISBEE_DESIGNS = [
|
||||||
|
{"name":"Interface", "model":"http://test.thoys.nl/hifi/models/frisbee/frisbee.fbx"},
|
||||||
|
{"name":"Pizza", "model":"http://test.thoys.nl/hifi/models/frisbee/pizza.fbx"},
|
||||||
|
{"name":"Swirl", "model":"http://test.thoys.nl/hifi/models/frisbee/swirl.fbx"},
|
||||||
|
{"name":"Mayan", "model":"http://test.thoys.nl/hifi/models/frisbee/mayan.fbx"},
|
||||||
|
];
|
||||||
|
const FRISBEE_MENU_DESIGN_POSTFIX = " Design";
|
||||||
|
const FRISBEE_DESIGN_RANDOM = "Random";
|
||||||
|
|
||||||
|
const SPIN_MULTIPLIER = 1000;
|
||||||
|
const FRISBEE_LIFETIME = 300; // 5 minutes
|
||||||
|
|
||||||
|
var windowDimensions = Controller.getViewportDimensions();
|
||||||
|
var toolHeight = 50;
|
||||||
|
var toolWidth = 50;
|
||||||
|
var frisbeeToggle;
|
||||||
|
var toolBar;
|
||||||
|
var frisbeeEnabled = true;
|
||||||
|
var newfrisbeeEnabled = false;
|
||||||
|
var forceMouseControls = false;
|
||||||
|
var hydrasConnected = false;
|
||||||
|
var selectedDesign = FRISBEE_DESIGN_RANDOM;
|
||||||
|
|
||||||
|
function loadSettings() {
|
||||||
|
frisbeeEnabled = Settings.getValue(FRISBEE_ENABLED_SETTING, "true") == "true";
|
||||||
|
newfrisbeeEnabled = Settings.getValue(FRISBEE_CREATENEW_SETTING, "false") == "true";
|
||||||
|
forceMouseControls = Settings.getValue(FRISBEE_FORCE_MOUSE_CONTROLS_SETTING, "false") == "true";
|
||||||
|
selectedDesign = Settings.getValue(FRISBEE_DESIGN_SETTING, "Random");
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSettings() {
|
||||||
|
Settings.setValue(FRISBEE_ENABLED_SETTING, frisbeeEnabled ? "true" : "false");
|
||||||
|
Settings.setValue(FRISBEE_CREATENEW_SETTING, newfrisbeeEnabled ? "true" : "false");
|
||||||
|
Settings.setValue(FRISBEE_FORCE_MOUSE_CONTROLS_SETTING, forceMouseControls ? "true" : "false");
|
||||||
|
Settings.setValue(FRISBEE_DESIGN_SETTING, selectedDesign);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveOverlays() {
|
||||||
|
var newViewPort = Controller.getViewportDimensions();
|
||||||
|
if (typeof(toolBar) === 'undefined') {
|
||||||
|
initToolBar();
|
||||||
|
} else if (windowDimensions.x == newViewPort.x &&
|
||||||
|
windowDimensions.y == newViewPort.y) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
windowDimensions = newViewPort;
|
||||||
|
var toolsX = windowDimensions.x - 8 - toolBar.width;
|
||||||
|
var toolsY = (windowDimensions.y - toolBar.height) / 2 + 80;
|
||||||
|
toolBar.move(toolsX, toolsY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function frisbeeURL() {
|
||||||
|
return selectedDesign == FRISBEE_DESIGN_RANDOM ? FRISBEE_DESIGNS[Math.floor(Math.random() * FRISBEE_DESIGNS.length)].model : getFrisbee(selectedDesign).model;
|
||||||
|
}
|
||||||
|
|
||||||
|
//This function checks if the modelURL is inside of our Designs or contains "frisbee" in it.
|
||||||
|
function validFrisbeeURL(frisbeeURL) {
|
||||||
|
for (var frisbee in FRISBEE_DESIGNS) {
|
||||||
|
if (FRISBEE_DESIGNS[frisbee].model == frisbeeURL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frisbeeURL.toLowerCase().indexOf("frisbee") !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFrisbee(frisbeeName) {
|
||||||
|
for (var frisbee in FRISBEE_DESIGNS) {
|
||||||
|
if (FRISBEE_DESIGNS[frisbee].name == frisbeeName) {
|
||||||
|
return FRISBEE_DESIGNS[frisbee];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Hand(name, palm, tip, forwardButton, button3, trigger) {
|
||||||
|
this.name = name;
|
||||||
|
this.palm = palm;
|
||||||
|
this.tip = tip;
|
||||||
|
this.forwardButton = forwardButton;
|
||||||
|
this.button3 = button3;
|
||||||
|
this.trigger = trigger;
|
||||||
|
this.holdingFrisbee = false;
|
||||||
|
this.particle = false;
|
||||||
|
this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); }
|
||||||
|
this.grabButtonPressed = function() {
|
||||||
|
return (
|
||||||
|
Controller.isButtonPressed(this.forwardButton) ||
|
||||||
|
Controller.isButtonPressed(this.button3) ||
|
||||||
|
Controller.getTriggerValue(this.trigger) > 0.5
|
||||||
|
)
|
||||||
|
};
|
||||||
|
this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); };
|
||||||
|
this.holdRotation = function() {
|
||||||
|
var q = Controller.getSpatialControlRawRotation(this.palm);
|
||||||
|
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); };
|
||||||
|
}
|
||||||
|
|
||||||
|
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 leftMouseControl = new MouseControl("LEFT");
|
||||||
|
var middleMouseControl = new MouseControl("MIDDLE");
|
||||||
|
var rightMouseControl = new MouseControl("RIGHT");
|
||||||
|
var mouseControls = [leftMouseControl, middleMouseControl, rightMouseControl];
|
||||||
|
var currentMouseControl = false;
|
||||||
|
|
||||||
|
var newSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw");
|
||||||
|
var catchSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw");
|
||||||
|
var throwSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw");
|
||||||
|
|
||||||
|
var simulatedFrisbees = [];
|
||||||
|
|
||||||
|
var wantDebugging = false;
|
||||||
|
function debugPrint(message) {
|
||||||
|
if (wantDebugging) {
|
||||||
|
print(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playSound(sound, position) {
|
||||||
|
if (!SOUNDS_ENABLED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var options = new AudioInjectionOptions();
|
||||||
|
options.position = position;
|
||||||
|
options.volume = 1.0;
|
||||||
|
Audio.playSound(sound, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupFrisbees() {
|
||||||
|
simulatedFrisbees = [];
|
||||||
|
var particles = Particles.findParticles(MyAvatar.position, 1000);
|
||||||
|
for (particle in particles) {
|
||||||
|
Particles.deleteParticle(particles[particle]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkControllerSide(hand) {
|
||||||
|
// If I don't currently have a frisbee in my hand, then try to catch closest one
|
||||||
|
if (!hand.holdingFrisbee && hand.grabButtonPressed()) {
|
||||||
|
var closestParticle = Particles.findClosestParticle(hand.palmPosition(), CATCH_RADIUS);
|
||||||
|
var modelUrl = Particles.getParticleProperties(closestParticle).modelURL;
|
||||||
|
if (closestParticle.isKnownID && validFrisbeeURL(Particles.getParticleProperties(closestParticle).modelURL)) {
|
||||||
|
Particles.editParticle(closestParticle, {modelScale: 1, inHand: true, position: hand.holdPosition(), shouldDie: true});
|
||||||
|
Particles.deleteParticle(closestParticle);
|
||||||
|
debugPrint(hand.message + " HAND- CAUGHT SOMETHING!!");
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
position: hand.holdPosition(),
|
||||||
|
velocity: { x: 0, y: 0, z: 0},
|
||||||
|
gravity: { x: 0, y: 0, z: 0},
|
||||||
|
inHand: true,
|
||||||
|
radius: FRISBEE_RADIUS,
|
||||||
|
damping: 0.999,
|
||||||
|
modelURL: modelUrl,
|
||||||
|
modelScale: FRISBEE_MODEL_SCALE,
|
||||||
|
modelRotation: hand.holdRotation(),
|
||||||
|
lifetime: FRISBEE_LIFETIME
|
||||||
|
};
|
||||||
|
|
||||||
|
newParticle = Particles.addParticle(properties);
|
||||||
|
|
||||||
|
hand.holdingFrisbee = true;
|
||||||
|
hand.particle = newParticle;
|
||||||
|
|
||||||
|
playSound(catchSound, hand.holdPosition());
|
||||||
|
|
||||||
|
return; // exit early
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If '3' is pressed, and not holding a frisbee, make a new one
|
||||||
|
if (hand.grabButtonPressed() && !hand.holdingFrisbee && newfrisbeeEnabled) {
|
||||||
|
var properties = {
|
||||||
|
position: hand.holdPosition(),
|
||||||
|
velocity: { x: 0, y: 0, z: 0},
|
||||||
|
gravity: { x: 0, y: 0, z: 0},
|
||||||
|
inHand: true,
|
||||||
|
radius: FRISBEE_RADIUS,
|
||||||
|
damping: 0.999,
|
||||||
|
modelURL: frisbeeURL(),
|
||||||
|
modelScale: FRISBEE_MODEL_SCALE,
|
||||||
|
modelRotation: hand.holdRotation(),
|
||||||
|
lifetime: FRISBEE_LIFETIME
|
||||||
|
};
|
||||||
|
|
||||||
|
newParticle = Particles.addParticle(properties);
|
||||||
|
hand.holdingFrisbee = true;
|
||||||
|
hand.particle = newParticle;
|
||||||
|
|
||||||
|
// Play a new frisbee sound
|
||||||
|
playSound(newSound, hand.holdPosition());
|
||||||
|
|
||||||
|
return; // exit early
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hand.holdingFrisbee) {
|
||||||
|
// If holding the frisbee keep it in the palm
|
||||||
|
if (hand.grabButtonPressed()) {
|
||||||
|
debugPrint(">>>>> " + hand.name + "-FRISBEE IN HAND, grabbing, hold and move");
|
||||||
|
var properties = {
|
||||||
|
position: hand.holdPosition(),
|
||||||
|
modelRotation: hand.holdRotation()
|
||||||
|
};
|
||||||
|
Particles.editParticle(hand.particle, properties);
|
||||||
|
} else {
|
||||||
|
debugPrint(">>>>> " + hand.name + "-FRISBEE IN HAND, not grabbing, THROW!!!");
|
||||||
|
// If frisbee just released, add velocity to it!
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
velocity: Vec3.multiply(hand.tipVelocity(), THROWN_VELOCITY_SCALING),
|
||||||
|
inHand: false,
|
||||||
|
lifetime: FRISBEE_LIFETIME,
|
||||||
|
gravity: { x: 0, y: -GRAVITY_STRENGTH, z: 0},
|
||||||
|
modelRotation: hand.holdRotation()
|
||||||
|
};
|
||||||
|
|
||||||
|
Particles.editParticle(hand.particle, properties);
|
||||||
|
|
||||||
|
simulatedFrisbees.push(hand.particle);
|
||||||
|
|
||||||
|
hand.holdingFrisbee = false;
|
||||||
|
hand.particle = false;
|
||||||
|
|
||||||
|
playSound(throwSound, hand.holdPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initToolBar() {
|
||||||
|
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
|
||||||
|
frisbeeToggle = toolBar.addTool({
|
||||||
|
imageURL: FRISBEE_BUTTON_URL,
|
||||||
|
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||||
|
width: toolWidth,
|
||||||
|
height: toolHeight,
|
||||||
|
visible: true,
|
||||||
|
alpha: 0.9
|
||||||
|
}, true);
|
||||||
|
enableNewFrisbee(newfrisbeeEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 hydrasConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkController(deltaTime) {
|
||||||
|
moveOverlays();
|
||||||
|
if (!frisbeeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// this is expected for hydras
|
||||||
|
if (hydraCheck()) {
|
||||||
|
checkControllerSide(leftHand);
|
||||||
|
checkControllerSide(rightHand);
|
||||||
|
}
|
||||||
|
if (!hydrasConnected || forceMouseControls) {
|
||||||
|
//TODO: add mouse cursor control code here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function controlFrisbees(deltaTime) {
|
||||||
|
var killSimulations = [];
|
||||||
|
for (frisbee in simulatedFrisbees) {
|
||||||
|
var properties = Particles.getParticleProperties(simulatedFrisbees[frisbee]);
|
||||||
|
//get the horizon length from the velocity origin in order to get speed
|
||||||
|
var speed = Vec3.length({x:properties.velocity.x, y:0, z:properties.velocity.z});
|
||||||
|
if (speed < MIN_SIMULATION_SPEED) {
|
||||||
|
//kill the frisbee simulation when speed is low
|
||||||
|
killSimulations.push(frisbee);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Particles.editParticle(simulatedFrisbees[frisbee], {modelRotation: Quat.multiply(properties.modelRotation, Quat.fromPitchYawRollDegrees(0, speed * deltaTime * SPIN_MULTIPLIER, 0))});
|
||||||
|
|
||||||
|
}
|
||||||
|
for (var i = killSimulations.length - 1; i >= 0; i--) {
|
||||||
|
simulatedFrisbees.splice(killSimulations[i], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//catches interfering calls of hydra-cursors
|
||||||
|
function withinBounds(coords) {
|
||||||
|
return coords.x >= 0 && coords.x < windowDimensions.x && coords.y >= 0 && coords.y < windowDimensions.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseMoveEvent(event) {
|
||||||
|
//TODO: mouse controls //print(withinBounds(event)); //print("move"+event.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mousePressEvent(event) {
|
||||||
|
print(event.x);
|
||||||
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||||
|
if (frisbeeToggle == toolBar.clicked(clickedOverlay)) {
|
||||||
|
newfrisbeeEnabled = !newfrisbeeEnabled;
|
||||||
|
saveSettings();
|
||||||
|
enableNewFrisbee(newfrisbeeEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableNewFrisbee(enable) {
|
||||||
|
if (toolBar.numberOfTools() > 0) {
|
||||||
|
toolBar.tools[0].select(enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseReleaseEvent(event) {
|
||||||
|
//TODO: mouse controls //print(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMenus() {
|
||||||
|
Menu.addMenu(FRISBEE_MENU);
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: FRISBEE_MENU,
|
||||||
|
menuItemName: "Frisbee Enabled",
|
||||||
|
isCheckable: true,
|
||||||
|
isChecked: frisbeeEnabled
|
||||||
|
});
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: FRISBEE_MENU,
|
||||||
|
menuItemName: "Cleanup Frisbees"
|
||||||
|
});
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: FRISBEE_MENU,
|
||||||
|
menuItemName: "Force Mouse Controls",
|
||||||
|
isCheckable: true,
|
||||||
|
isChecked: forceMouseControls
|
||||||
|
});
|
||||||
|
Menu.addMenu(FRISBEE_DESIGN_MENU);
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: FRISBEE_DESIGN_MENU,
|
||||||
|
menuItemName: FRISBEE_DESIGN_RANDOM + FRISBEE_MENU_DESIGN_POSTFIX,
|
||||||
|
isCheckable: true,
|
||||||
|
isChecked: selectedDesign == FRISBEE_DESIGN_RANDOM
|
||||||
|
});
|
||||||
|
for (frisbee in FRISBEE_DESIGNS) {
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: FRISBEE_DESIGN_MENU,
|
||||||
|
menuItemName: FRISBEE_DESIGNS[frisbee].name + FRISBEE_MENU_DESIGN_POSTFIX,
|
||||||
|
isCheckable: true,
|
||||||
|
isChecked: selectedDesign == FRISBEE_DESIGNS[frisbee].name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//startup calls:
|
||||||
|
loadSettings();
|
||||||
|
setupMenus();
|
||||||
|
function scriptEnding() {
|
||||||
|
toolBar.cleanup();
|
||||||
|
Menu.removeMenu(FRISBEE_MENU);
|
||||||
|
}
|
||||||
|
|
||||||
|
function menuItemEvent(menuItem) {
|
||||||
|
if (menuItem == "Cleanup Frisbees") {
|
||||||
|
cleanupFrisbees();
|
||||||
|
return;
|
||||||
|
} else if (menuItem == "Frisbee Enabled") {
|
||||||
|
frisbeeEnabled = Menu.isOptionChecked(menuItem);
|
||||||
|
saveSettings();
|
||||||
|
return;
|
||||||
|
} else if (menuItem == "Force Mouse Controls") {
|
||||||
|
forceMouseControls = Menu.isOptionChecked(menuItem);
|
||||||
|
saveSettings();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (menuItem.indexOf(FRISBEE_MENU_DESIGN_POSTFIX, menuItem.length - FRISBEE_MENU_DESIGN_POSTFIX.length) !== -1) {
|
||||||
|
var item_name = menuItem.substring(0, menuItem.length - FRISBEE_MENU_DESIGN_POSTFIX.length);
|
||||||
|
if (item_name == FRISBEE_DESIGN_RANDOM || getFrisbee(item_name) != undefined) {
|
||||||
|
Menu.setIsOptionChecked(selectedDesign + FRISBEE_MENU_DESIGN_POSTFIX, false);
|
||||||
|
selectedDesign = item_name;
|
||||||
|
saveSettings();
|
||||||
|
Menu.setIsOptionChecked(selectedDesign + FRISBEE_MENU_DESIGN_POSTFIX, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the call back so it fires before each data send
|
||||||
|
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||||
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
|
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||||
|
Menu.menuItemEvent.connect(menuItemEvent);
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
Script.update.connect(checkController);
|
||||||
|
Script.update.connect(controlFrisbees);
|
|
@ -21,10 +21,10 @@ var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.pos
|
||||||
var joysticksCaptured = false;
|
var joysticksCaptured = false;
|
||||||
var THRUST_CONTROLLER = 0;
|
var THRUST_CONTROLLER = 0;
|
||||||
var VIEW_CONTROLLER = 1;
|
var VIEW_CONTROLLER = 1;
|
||||||
var INITIAL_THRUST_MULTPLIER = 1.0;
|
var INITIAL_THRUST_MULTIPLIER = 1.0;
|
||||||
var THRUST_INCREASE_RATE = 1.05;
|
var THRUST_INCREASE_RATE = 1.05;
|
||||||
var MAX_THRUST_MULTIPLIER = 75.0;
|
var MAX_THRUST_MULTIPLIER = 75.0;
|
||||||
var thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
var thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
|
||||||
var grabDelta = { x: 0, y: 0, z: 0};
|
var grabDelta = { x: 0, y: 0, z: 0};
|
||||||
var grabStartPosition = { x: 0, y: 0, z: 0};
|
var grabStartPosition = { x: 0, y: 0, z: 0};
|
||||||
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||||
|
@ -34,6 +34,8 @@ var grabbingWithRightHand = false;
|
||||||
var wasGrabbingWithRightHand = false;
|
var wasGrabbingWithRightHand = false;
|
||||||
var grabbingWithLeftHand = false;
|
var grabbingWithLeftHand = false;
|
||||||
var wasGrabbingWithLeftHand = false;
|
var wasGrabbingWithLeftHand = false;
|
||||||
|
var movingWithHead = false;
|
||||||
|
var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw;
|
||||||
var EPSILON = 0.000001;
|
var EPSILON = 0.000001;
|
||||||
var velocity = { x: 0, y: 0, z: 0};
|
var velocity = { x: 0, y: 0, z: 0};
|
||||||
var THRUST_MAG_UP = 100.0;
|
var THRUST_MAG_UP = 100.0;
|
||||||
|
@ -241,6 +243,47 @@ function handleGrabBehavior(deltaTime) {
|
||||||
wasGrabbingWithLeftHand = grabbingWithLeftHand;
|
wasGrabbingWithLeftHand = grabbingWithLeftHand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var HEAD_MOVE_DEAD_ZONE = 0.0;
|
||||||
|
var HEAD_STRAFE_DEAD_ZONE = 0.0;
|
||||||
|
var HEAD_ROTATE_DEAD_ZONE = 0.0;
|
||||||
|
var HEAD_THRUST_FWD_SCALE = 12000.0;
|
||||||
|
var HEAD_THRUST_STRAFE_SCALE = 1000.0;
|
||||||
|
var HEAD_YAW_RATE = 2.0;
|
||||||
|
var HEAD_PITCH_RATE = 1.0;
|
||||||
|
var HEAD_ROLL_THRUST_SCALE = 75.0;
|
||||||
|
var HEAD_PITCH_LIFT_THRUST = 3.0;
|
||||||
|
|
||||||
|
function moveWithHead(deltaTime) {
|
||||||
|
if (movingWithHead) {
|
||||||
|
var deltaYaw = MyAvatar.getHeadFinalYaw() - headStartYaw;
|
||||||
|
var deltaPitch = MyAvatar.getHeadDeltaPitch() - headStartDeltaPitch;
|
||||||
|
|
||||||
|
var bodyLocalCurrentHeadVector = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position);
|
||||||
|
bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.angleAxis(-deltaYaw, {x:0, y: 1, z:0}), bodyLocalCurrentHeadVector);
|
||||||
|
var headDelta = Vec3.subtract(bodyLocalCurrentHeadVector, headStartPosition);
|
||||||
|
headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta);
|
||||||
|
headDelta.y = 0.0; // Don't respond to any of the vertical component of head motion
|
||||||
|
|
||||||
|
// Thrust based on leaning forward and side-to-side
|
||||||
|
if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) {
|
||||||
|
MyAvatar.addThrust(Vec3.multiply(Quat.getFront(Camera.getOrientation()), -headDelta.z * HEAD_THRUST_FWD_SCALE * deltaTime));
|
||||||
|
}
|
||||||
|
if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) {
|
||||||
|
MyAvatar.addThrust(Vec3.multiply(Quat.getRight(Camera.getOrientation()), headDelta.x * HEAD_THRUST_STRAFE_SCALE * deltaTime));
|
||||||
|
}
|
||||||
|
if (Math.abs(deltaYaw) > HEAD_ROTATE_DEAD_ZONE) {
|
||||||
|
var orientation = Quat.multiply(Quat.angleAxis(deltaYaw * HEAD_YAW_RATE * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation);
|
||||||
|
MyAvatar.orientation = orientation;
|
||||||
|
}
|
||||||
|
// Thrust Up/Down based on head pitch
|
||||||
|
MyAvatar.addThrust(Vec3.multiply({ x:0, y:1, z:0 }, (MyAvatar.getHeadFinalPitch() - headStartFinalPitch) * HEAD_PITCH_LIFT_THRUST * deltaTime));
|
||||||
|
// For head trackers, adjust pitch by head pitch
|
||||||
|
MyAvatar.headPitch += deltaPitch * HEAD_PITCH_RATE * deltaTime;
|
||||||
|
// Thrust strafe based on roll ange
|
||||||
|
MyAvatar.addThrust(Vec3.multiply(Quat.getRight(Camera.getOrientation()), -(MyAvatar.getHeadFinalRoll() - headStartRoll) * HEAD_ROLL_THRUST_SCALE * deltaTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update for joysticks and move button
|
// Update for joysticks and move button
|
||||||
function flyWithHydra(deltaTime) {
|
function flyWithHydra(deltaTime) {
|
||||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||||
|
@ -262,7 +305,7 @@ function flyWithHydra(deltaTime) {
|
||||||
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
||||||
MyAvatar.addThrust(thrustRight);
|
MyAvatar.addThrust(thrustRight);
|
||||||
} else {
|
} else {
|
||||||
thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// View Controller
|
// View Controller
|
||||||
|
@ -280,6 +323,7 @@ function flyWithHydra(deltaTime) {
|
||||||
MyAvatar.headPitch = newPitch;
|
MyAvatar.headPitch = newPitch;
|
||||||
}
|
}
|
||||||
handleGrabBehavior(deltaTime);
|
handleGrabBehavior(deltaTime);
|
||||||
|
moveWithHead(deltaTime);
|
||||||
displayDebug();
|
displayDebug();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -296,3 +340,19 @@ function scriptEnding() {
|
||||||
}
|
}
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
||||||
|
Controller.keyPressEvent.connect(function(event) {
|
||||||
|
if (event.text == "SPACE" && !movingWithHead) {
|
||||||
|
movingWithHead = true;
|
||||||
|
headStartPosition = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position);
|
||||||
|
headStartDeltaPitch = MyAvatar.getHeadDeltaPitch();
|
||||||
|
headStartFinalPitch = MyAvatar.getHeadFinalPitch();
|
||||||
|
headStartRoll = MyAvatar.getHeadFinalRoll();
|
||||||
|
headStartYaw = MyAvatar.getHeadFinalYaw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Controller.keyReleaseEvent.connect(function(event) {
|
||||||
|
if (event.text == "SPACE") {
|
||||||
|
movingWithHead = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
201
examples/speechControl.js
Normal file
201
examples/speechControl.js
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
//
|
||||||
|
// speechControl.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Ryan Huffman on 07/31/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var ACCELERATION = 80;
|
||||||
|
var STEP_DURATION = 1.0; // Duration of a step command in seconds
|
||||||
|
var TURN_DEGREES = 90;
|
||||||
|
var SLIGHT_TURN_DEGREES = 45;
|
||||||
|
var TURN_AROUND_DEGREES = 180;
|
||||||
|
var TURN_RATE = 90; // Turn rate in degrees per second
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/** COMMANDS *****************************************************************/
|
||||||
|
var CMD_MOVE_FORWARD = "Move forward";
|
||||||
|
var CMD_MOVE_BACKWARD = "Move backward";
|
||||||
|
var CMD_MOVE_UP = "Move up";
|
||||||
|
var CMD_MOVE_DOWN = "Move down";
|
||||||
|
var CMD_MOVE_LEFT = "Move left";
|
||||||
|
var CMD_MOVE_RIGHT = "Move right";
|
||||||
|
|
||||||
|
var CMD_STEP_FORWARD = "Step forward";
|
||||||
|
var CMD_STEP_BACKWARD = "Step backward";
|
||||||
|
var CMD_STEP_LEFT = "Step left";
|
||||||
|
var CMD_STEP_RIGHT = "Step right";
|
||||||
|
var CMD_STEP_UP = "Step up";
|
||||||
|
var CMD_STEP_DOWN = "Step down";
|
||||||
|
|
||||||
|
var CMD_TURN_LEFT = "Turn left";
|
||||||
|
var CMD_TURN_SLIGHT_LEFT = "Turn slight left";
|
||||||
|
var CMD_TURN_RIGHT = "Turn right";
|
||||||
|
var CMD_TURN_SLIGHT_RIGHT = "Turn slight right";
|
||||||
|
var CMD_TURN_AROUND = "Turn around";
|
||||||
|
|
||||||
|
var CMD_STOP = "Stop";
|
||||||
|
|
||||||
|
var CMD_SHOW_COMMANDS = "Show commands";
|
||||||
|
|
||||||
|
var MOVE_COMMANDS = [
|
||||||
|
CMD_MOVE_FORWARD,
|
||||||
|
CMD_MOVE_BACKWARD,
|
||||||
|
CMD_MOVE_UP,
|
||||||
|
CMD_MOVE_DOWN,
|
||||||
|
CMD_MOVE_LEFT,
|
||||||
|
CMD_MOVE_RIGHT,
|
||||||
|
];
|
||||||
|
|
||||||
|
var STEP_COMMANDS = [
|
||||||
|
CMD_STEP_FORWARD,
|
||||||
|
CMD_STEP_BACKWARD,
|
||||||
|
CMD_STEP_UP,
|
||||||
|
CMD_STEP_DOWN,
|
||||||
|
CMD_STEP_LEFT,
|
||||||
|
CMD_STEP_RIGHT,
|
||||||
|
];
|
||||||
|
|
||||||
|
var TURN_COMMANDS = [
|
||||||
|
CMD_TURN_LEFT,
|
||||||
|
CMD_TURN_SLIGHT_LEFT,
|
||||||
|
CMD_TURN_RIGHT,
|
||||||
|
CMD_TURN_SLIGHT_RIGHT,
|
||||||
|
CMD_TURN_AROUND,
|
||||||
|
];
|
||||||
|
|
||||||
|
var OTHER_COMMANDS = [
|
||||||
|
CMD_STOP,
|
||||||
|
CMD_SHOW_COMMANDS,
|
||||||
|
];
|
||||||
|
|
||||||
|
var ALL_COMMANDS = []
|
||||||
|
.concat(MOVE_COMMANDS)
|
||||||
|
.concat(STEP_COMMANDS)
|
||||||
|
.concat(TURN_COMMANDS)
|
||||||
|
.concat(OTHER_COMMANDS);
|
||||||
|
|
||||||
|
/** END OF COMMANDS **********************************************************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
var currentCommandFunc = null;
|
||||||
|
|
||||||
|
function handleCommandRecognized(command) {
|
||||||
|
if (MOVE_COMMANDS.indexOf(command) > -1 || STEP_COMMANDS.indexOf(command) > -1) {
|
||||||
|
// If this is a STEP_* command, we will want to countdown the duration
|
||||||
|
// of time to move. MOVE_* commands don't stop.
|
||||||
|
var timeRemaining = MOVE_COMMANDS.indexOf(command) > -1 ? 0 : STEP_DURATION;
|
||||||
|
var accel = { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
|
if (command == CMD_MOVE_FORWARD || command == CMD_STEP_FORWARD) {
|
||||||
|
accel = { x: 0, y: 0, z: 1 };
|
||||||
|
} else if (command == CMD_MOVE_BACKWARD || command == CMD_STEP_BACKWARD) {
|
||||||
|
accel = { x: 0, y: 0, z: -1 };
|
||||||
|
} else if (command === CMD_MOVE_UP || command == CMD_STEP_UP) {
|
||||||
|
accel = { x: 0, y: 1, z: 0 };
|
||||||
|
} else if (command == CMD_MOVE_DOWN || command == CMD_STEP_DOWN) {
|
||||||
|
accel = { x: 0, y: -1, z: 0 };
|
||||||
|
} else if (command == CMD_MOVE_LEFT || command == CMD_STEP_LEFT) {
|
||||||
|
accel = { x: -1, y: 0, z: 0 };
|
||||||
|
} else if (command == CMD_MOVE_RIGHT || command == CMD_STEP_RIGHT) {
|
||||||
|
accel = { x: 1, y: 0, z: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCommandFunc = function(dt) {
|
||||||
|
if (timeRemaining > 0 && dt >= timeRemaining) {
|
||||||
|
dt = timeRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
var headOrientation = MyAvatar.headOrientation;
|
||||||
|
var front = Quat.getFront(headOrientation);
|
||||||
|
var right = Quat.getRight(headOrientation);
|
||||||
|
var up = Quat.getUp(headOrientation);
|
||||||
|
|
||||||
|
var thrust = Vec3.multiply(front, accel.z * ACCELERATION);
|
||||||
|
thrust = Vec3.sum(thrust, Vec3.multiply(right, accel.x * ACCELERATION));
|
||||||
|
thrust = Vec3.sum(thrust, Vec3.multiply(up, accel.y * ACCELERATION));
|
||||||
|
MyAvatar.addThrust(thrust);
|
||||||
|
|
||||||
|
if (timeRemaining > 0) {
|
||||||
|
timeRemaining -= dt;
|
||||||
|
return timeRemaining > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
} else if (TURN_COMMANDS.indexOf(command) > -1) {
|
||||||
|
var degreesRemaining;
|
||||||
|
var sign;
|
||||||
|
if (command == CMD_TURN_LEFT) {
|
||||||
|
sign = 1;
|
||||||
|
degreesRemaining = TURN_DEGREES;
|
||||||
|
} else if (command == CMD_TURN_RIGHT) {
|
||||||
|
sign = -1;
|
||||||
|
degreesRemaining = TURN_DEGREES;
|
||||||
|
} else if (command == CMD_TURN_SLIGHT_LEFT) {
|
||||||
|
sign = 1;
|
||||||
|
degreesRemaining = SLIGHT_TURN_DEGREES;
|
||||||
|
} else if (command == CMD_TURN_SLIGHT_RIGHT) {
|
||||||
|
sign = -1;
|
||||||
|
degreesRemaining = SLIGHT_TURN_DEGREES;
|
||||||
|
} else if (command == CMD_TURN_AROUND) {
|
||||||
|
sign = 1;
|
||||||
|
degreesRemaining = TURN_AROUND_DEGREES;
|
||||||
|
}
|
||||||
|
currentCommandFunc = function(dt) {
|
||||||
|
// Determine how much to turn by
|
||||||
|
var turnAmount = TURN_RATE * dt;
|
||||||
|
if (turnAmount > degreesRemaining) {
|
||||||
|
turnAmount = degreesRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply turn
|
||||||
|
var orientation = MyAvatar.orientation;
|
||||||
|
var deltaOrientation = Quat.fromPitchYawRollDegrees(0, sign * turnAmount, 0);
|
||||||
|
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
|
||||||
|
|
||||||
|
degreesRemaining -= turnAmount;
|
||||||
|
return turnAmount > 0;
|
||||||
|
}
|
||||||
|
} else if (command == CMD_STOP) {
|
||||||
|
currentCommandFunc = null;
|
||||||
|
} else if (command == CMD_SHOW_COMMANDS) {
|
||||||
|
var msg = "";
|
||||||
|
for (var i = 0; i < ALL_COMMANDS.length; i++) {
|
||||||
|
msg += ALL_COMMANDS[i] + "\n";
|
||||||
|
}
|
||||||
|
Window.alert(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(dt) {
|
||||||
|
if (currentCommandFunc) {
|
||||||
|
if (currentCommandFunc(dt) === false) {
|
||||||
|
currentCommandFunc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
for (var i = 0; i < ALL_COMMANDS.length; i++) {
|
||||||
|
SpeechRecognizer.addCommand(ALL_COMMANDS[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
for (var i = 0; i < ALL_COMMANDS.length; i++) {
|
||||||
|
SpeechRecognizer.removeCommand(ALL_COMMANDS[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
Script.update.connect(update);
|
||||||
|
SpeechRecognizer.commandRecognized.connect(handleCommandRecognized);
|
||||||
|
|
||||||
|
setup();
|
|
@ -46,6 +46,15 @@ foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels pa
|
||||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}")
|
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}")
|
||||||
endforeach(SUBDIR)
|
endforeach(SUBDIR)
|
||||||
|
|
||||||
|
# Add SpeechRecognizer if on OS X, otherwise remove
|
||||||
|
if (APPLE)
|
||||||
|
file(GLOB INTERFACE_OBJCPP_SRCS "src/SpeechRecognizer.mm")
|
||||||
|
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${INTERFACE_OBJCPP_SRCS})
|
||||||
|
else ()
|
||||||
|
get_filename_component(SPEECHRECOGNIZER_H "src/SpeechRecognizer.h" ABSOLUTE)
|
||||||
|
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_H})
|
||||||
|
endif ()
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Script Svg WebKitWidgets)
|
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Script Svg WebKitWidgets)
|
||||||
|
|
||||||
# grab the ui files in resources/ui
|
# grab the ui files in resources/ui
|
||||||
|
@ -165,8 +174,9 @@ if (APPLE)
|
||||||
find_library(CoreFoundation CoreFoundation)
|
find_library(CoreFoundation CoreFoundation)
|
||||||
find_library(GLUT GLUT)
|
find_library(GLUT GLUT)
|
||||||
find_library(OpenGL OpenGL)
|
find_library(OpenGL OpenGL)
|
||||||
|
find_library(AppKit AppKit)
|
||||||
|
|
||||||
target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation} ${GLUT} ${OpenGL})
|
target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation} ${GLUT} ${OpenGL} ${AppKit})
|
||||||
|
|
||||||
# install command for OS X bundle
|
# install command for OS X bundle
|
||||||
INSTALL(TARGETS ${TARGET_NAME}
|
INSTALL(TARGETS ${TARGET_NAME}
|
||||||
|
|
|
@ -894,7 +894,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Qt::Key_Space:
|
case Qt::Key_Apostrophe:
|
||||||
resetSensors();
|
resetSensors();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1051,20 +1051,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
case Qt::Key_R:
|
case Qt::Key_R:
|
||||||
if (isShifted) {
|
if (isShifted) {
|
||||||
Menu::getInstance()->triggerOption(MenuOption::FrustumRenderMode);
|
Menu::getInstance()->triggerOption(MenuOption::FrustumRenderMode);
|
||||||
} else if (isMeta) {
|
|
||||||
if (_myAvatar->isRecording()) {
|
|
||||||
_myAvatar->stopRecording();
|
|
||||||
} else {
|
|
||||||
_myAvatar->startRecording();
|
|
||||||
_audio.setRecorder(_myAvatar->getRecorder());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_myAvatar->isPlaying()) {
|
|
||||||
_myAvatar->stopPlaying();
|
|
||||||
} else {
|
|
||||||
_myAvatar->startPlaying();
|
|
||||||
_audio.setPlayer(_myAvatar->getPlayer());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Percent:
|
case Qt::Key_Percent:
|
||||||
|
@ -3749,6 +3735,10 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
||||||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||||
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
|
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
scriptEngine->registerGlobalObject("SpeechRecognizer", Menu::getInstance()->getSpeechRecognizer());
|
||||||
|
#endif
|
||||||
|
|
||||||
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
|
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
|
||||||
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
||||||
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
||||||
|
|
|
@ -94,6 +94,9 @@ Menu::Menu() :
|
||||||
_octreeStatsDialog(NULL),
|
_octreeStatsDialog(NULL),
|
||||||
_lodToolsDialog(NULL),
|
_lodToolsDialog(NULL),
|
||||||
_userLocationsDialog(NULL),
|
_userLocationsDialog(NULL),
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
_speechRecognizer(),
|
||||||
|
#endif
|
||||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||||
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
||||||
|
@ -225,6 +228,12 @@ Menu::Menu() :
|
||||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
|
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
|
||||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, this, SLOT(showScriptEditor()));
|
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, this, SLOT(showScriptEditor()));
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech,
|
||||||
|
Qt::CTRL | Qt::SHIFT | Qt::Key_C, _speechRecognizer.getEnabled(), &_speechRecognizer, SLOT(setEnabled(bool)));
|
||||||
|
connect(&_speechRecognizer, SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_QXMPP
|
#ifdef HAVE_QXMPP
|
||||||
_chatAction = addActionToQMenuAndActionHash(toolsMenu,
|
_chatAction = addActionToQMenuAndActionHash(toolsMenu,
|
||||||
MenuOption::Chat,
|
MenuOption::Chat,
|
||||||
|
@ -688,6 +697,10 @@ void Menu::loadSettings(QSettings* settings) {
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).toString();
|
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).toString();
|
||||||
setScriptsLocation(settings->value("scriptsLocation", QString()).toString());
|
setScriptsLocation(settings->value("scriptsLocation", QString()).toString());
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
_speechRecognizer.setEnabled(settings->value("speechRecognitionEnabled", false).toBool());
|
||||||
|
#endif
|
||||||
|
|
||||||
settings->beginGroup("View Frustum Offset Camera");
|
settings->beginGroup("View Frustum Offset Camera");
|
||||||
// in case settings is corrupt or missing loadSetting() will check for NaN
|
// in case settings is corrupt or missing loadSetting() will check for NaN
|
||||||
_viewFrustumOffset.yaw = loadSetting(settings, "viewFrustumOffsetYaw", 0.0f);
|
_viewFrustumOffset.yaw = loadSetting(settings, "viewFrustumOffsetYaw", 0.0f);
|
||||||
|
@ -735,6 +748,9 @@ void Menu::saveSettings(QSettings* settings) {
|
||||||
settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust);
|
settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust);
|
||||||
settings->setValue("snapshotsLocation", _snapshotsLocation);
|
settings->setValue("snapshotsLocation", _snapshotsLocation);
|
||||||
settings->setValue("scriptsLocation", _scriptsLocation);
|
settings->setValue("scriptsLocation", _scriptsLocation);
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
settings->setValue("speechRecognitionEnabled", _speechRecognizer.getEnabled());
|
||||||
|
#endif
|
||||||
settings->beginGroup("View Frustum Offset Camera");
|
settings->beginGroup("View Frustum Offset Camera");
|
||||||
settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw);
|
settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw);
|
||||||
settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch);
|
settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch);
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
#include <MenuItemProperties.h>
|
#include <MenuItemProperties.h>
|
||||||
#include <OctreeConstants.h>
|
#include <OctreeConstants.h>
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include "SpeechRecognizer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "location/LocationManager.h"
|
#include "location/LocationManager.h"
|
||||||
#include "ui/PreferencesDialog.h"
|
#include "ui/PreferencesDialog.h"
|
||||||
#include "ui/ChatWindow.h"
|
#include "ui/ChatWindow.h"
|
||||||
|
@ -137,6 +141,10 @@ public:
|
||||||
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
SpeechRecognizer* getSpeechRecognizer() { return &_speechRecognizer; }
|
||||||
|
#endif
|
||||||
|
|
||||||
// User Tweakable PPS from Voxel Server
|
// User Tweakable PPS from Voxel Server
|
||||||
int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; }
|
int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; }
|
||||||
void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; }
|
void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; }
|
||||||
|
@ -272,6 +280,9 @@ private:
|
||||||
OctreeStatsDialog* _octreeStatsDialog;
|
OctreeStatsDialog* _octreeStatsDialog;
|
||||||
LodToolsDialog* _lodToolsDialog;
|
LodToolsDialog* _lodToolsDialog;
|
||||||
UserLocationsDialog* _userLocationsDialog;
|
UserLocationsDialog* _userLocationsDialog;
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
SpeechRecognizer _speechRecognizer;
|
||||||
|
#endif
|
||||||
int _maxVoxels;
|
int _maxVoxels;
|
||||||
float _voxelSizeScale;
|
float _voxelSizeScale;
|
||||||
float _oculusUIAngularSize;
|
float _oculusUIAngularSize;
|
||||||
|
@ -351,6 +362,7 @@ namespace MenuOption {
|
||||||
const QString CollideWithVoxels = "Collide With Voxels";
|
const QString CollideWithVoxels = "Collide With Voxels";
|
||||||
const QString Collisions = "Collisions";
|
const QString Collisions = "Collisions";
|
||||||
const QString Console = "Console...";
|
const QString Console = "Console...";
|
||||||
|
const QString ControlWithSpeech = "Control With Speech";
|
||||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||||
const QString DisableActivityLogger = "Disable Activity Logger";
|
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include "Recorder.h"
|
#include "Recorder.h"
|
||||||
|
|
||||||
|
@ -221,7 +223,7 @@ void Player::startPlaying() {
|
||||||
_audioThread = new QThread();
|
_audioThread = new QThread();
|
||||||
_options.setPosition(_avatar->getPosition());
|
_options.setPosition(_avatar->getPosition());
|
||||||
_options.setOrientation(_avatar->getOrientation());
|
_options.setOrientation(_avatar->getOrientation());
|
||||||
_injector.reset(new AudioInjector(_recording->getAudio(), _options), &QObject::deleteLater);
|
_injector.reset(new AudioInjector(_recording->getAudio(), _options));
|
||||||
_injector->moveToThread(_audioThread);
|
_injector->moveToThread(_audioThread);
|
||||||
_audioThread->start();
|
_audioThread->start();
|
||||||
QMetaObject::invokeMethod(_injector.data(), "injectAudio", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(_injector.data(), "injectAudio", Qt::QueuedConnection);
|
||||||
|
@ -241,9 +243,14 @@ void Player::stopPlaying() {
|
||||||
|
|
||||||
// Cleanup audio thread
|
// Cleanup audio thread
|
||||||
_injector->stop();
|
_injector->stop();
|
||||||
|
QObject::connect(_injector.data(), &AudioInjector::finished,
|
||||||
|
_injector.data(), &AudioInjector::deleteLater);
|
||||||
|
QObject::connect(_injector.data(), &AudioInjector::destroyed,
|
||||||
|
_audioThread, &QThread::quit);
|
||||||
|
QObject::connect(_audioThread, &QThread::finished,
|
||||||
|
_audioThread, &QThread::deleteLater);
|
||||||
_injector.clear();
|
_injector.clear();
|
||||||
_audioThread->exit();
|
_audioThread = NULL;
|
||||||
_audioThread->deleteLater();
|
|
||||||
qDebug() << "Recorder::stopPlaying()";
|
qDebug() << "Recorder::stopPlaying()";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,13 +316,249 @@ bool Player::computeCurrentFrame() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeRecordingToFile(RecordingPointer recording, QString file) {
|
void writeRecordingToFile(RecordingPointer recording, QString filename) {
|
||||||
// TODO
|
qDebug() << "Writing recording to " << filename;
|
||||||
qDebug() << "Writing recording to " << file;
|
QElapsedTimer timer;
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << file.fileName();
|
||||||
|
|
||||||
|
|
||||||
|
QDataStream fileStream(&file);
|
||||||
|
|
||||||
|
fileStream << recording->_timestamps;
|
||||||
|
|
||||||
|
RecordingFrame& baseFrame = recording->_frames[0];
|
||||||
|
int totalLength = 0;
|
||||||
|
|
||||||
|
// Blendshape coefficients
|
||||||
|
fileStream << baseFrame._blendshapeCoefficients;
|
||||||
|
totalLength += baseFrame._blendshapeCoefficients.size();
|
||||||
|
|
||||||
|
// Joint Rotations
|
||||||
|
int jointRotationSize = baseFrame._jointRotations.size();
|
||||||
|
fileStream << jointRotationSize;
|
||||||
|
for (int i = 0; i < jointRotationSize; ++i) {
|
||||||
|
fileStream << baseFrame._jointRotations[i].x << baseFrame._jointRotations[i].y << baseFrame._jointRotations[i].z << baseFrame._jointRotations[i].w;
|
||||||
|
}
|
||||||
|
totalLength += jointRotationSize;
|
||||||
|
|
||||||
|
// Translation
|
||||||
|
fileStream << baseFrame._translation.x << baseFrame._translation.y << baseFrame._translation.z;
|
||||||
|
totalLength += 1;
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
fileStream << baseFrame._rotation.x << baseFrame._rotation.y << baseFrame._rotation.z << baseFrame._rotation.w;
|
||||||
|
totalLength += 1;
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
fileStream << baseFrame._scale;
|
||||||
|
totalLength += 1;
|
||||||
|
|
||||||
|
// Head Rotation
|
||||||
|
fileStream << baseFrame._headRotation.x << baseFrame._headRotation.y << baseFrame._headRotation.z << baseFrame._headRotation.w;
|
||||||
|
totalLength += 1;
|
||||||
|
|
||||||
|
// Lean Sideways
|
||||||
|
fileStream << baseFrame._leanSideways;
|
||||||
|
totalLength += 1;
|
||||||
|
|
||||||
|
// Lean Forward
|
||||||
|
fileStream << baseFrame._leanForward;
|
||||||
|
totalLength += 1;
|
||||||
|
|
||||||
|
for (int i = 1; i < recording->_timestamps.size(); ++i) {
|
||||||
|
QBitArray mask(totalLength);
|
||||||
|
int maskIndex = 0;
|
||||||
|
QByteArray buffer;
|
||||||
|
QDataStream stream(&buffer, QIODevice::WriteOnly);
|
||||||
|
RecordingFrame& previousFrame = recording->_frames[i - 1];
|
||||||
|
RecordingFrame& frame = recording->_frames[i];
|
||||||
|
|
||||||
|
// Blendshape coefficients
|
||||||
|
for (int i = 0; i < frame._blendshapeCoefficients.size(); ++i) {
|
||||||
|
if (frame._blendshapeCoefficients[i] != previousFrame._blendshapeCoefficients[i]) {
|
||||||
|
stream << frame._blendshapeCoefficients[i];
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joint Rotations
|
||||||
|
for (int i = 0; i < frame._jointRotations.size(); ++i) {
|
||||||
|
if (frame._jointRotations[i] != previousFrame._jointRotations[i]) {
|
||||||
|
stream << frame._jointRotations[i].x << frame._jointRotations[i].y << frame._jointRotations[i].z << frame._jointRotations[i].w;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translation
|
||||||
|
if (frame._translation != previousFrame._translation) {
|
||||||
|
stream << frame._translation.x << frame._translation.y << frame._translation.z;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
if (frame._rotation != previousFrame._rotation) {
|
||||||
|
stream << frame._rotation.x << frame._rotation.y << frame._rotation.z << frame._rotation.w;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
if (frame._scale != previousFrame._scale) {
|
||||||
|
stream << frame._scale;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
|
||||||
|
// Head Rotation
|
||||||
|
if (frame._headRotation != previousFrame._headRotation) {
|
||||||
|
stream << frame._headRotation.x << frame._headRotation.y << frame._headRotation.z << frame._headRotation.w;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
|
||||||
|
// Lean Sideways
|
||||||
|
if (frame._leanSideways != previousFrame._leanSideways) {
|
||||||
|
stream << frame._leanSideways;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
|
||||||
|
// Lean Forward
|
||||||
|
if (frame._leanForward != previousFrame._leanForward) {
|
||||||
|
stream << frame._leanForward;
|
||||||
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
|
||||||
|
fileStream << mask;
|
||||||
|
fileStream << buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream << recording->_audio->getByteArray();
|
||||||
|
|
||||||
|
qDebug() << "Wrote " << file.size() << " bytes in " << timer.elapsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordingPointer readRecordingFromFile(RecordingPointer recording, QString file) {
|
RecordingPointer readRecordingFromFile(RecordingPointer recording, QString filename) {
|
||||||
// TODO
|
qDebug() << "Reading recording from " << filename;
|
||||||
qDebug() << "Reading recording from " << file;
|
if (!recording) {
|
||||||
|
recording.reset(new Recording());
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)){
|
||||||
|
return recording;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream fileStream(&file);
|
||||||
|
|
||||||
|
fileStream >> recording->_timestamps;
|
||||||
|
RecordingFrame baseFrame;
|
||||||
|
|
||||||
|
// Blendshape coefficients
|
||||||
|
fileStream >> baseFrame._blendshapeCoefficients;
|
||||||
|
|
||||||
|
// Joint Rotations
|
||||||
|
int jointRotationSize;
|
||||||
|
fileStream >> jointRotationSize;
|
||||||
|
baseFrame._jointRotations.resize(jointRotationSize);
|
||||||
|
for (int i = 0; i < jointRotationSize; ++i) {
|
||||||
|
fileStream >> baseFrame._jointRotations[i].x >> baseFrame._jointRotations[i].y >> baseFrame._jointRotations[i].z >> baseFrame._jointRotations[i].w;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream >> baseFrame._translation.x >> baseFrame._translation.y >> baseFrame._translation.z;
|
||||||
|
fileStream >> baseFrame._rotation.x >> baseFrame._rotation.y >> baseFrame._rotation.z >> baseFrame._rotation.w;
|
||||||
|
fileStream >> baseFrame._scale;
|
||||||
|
fileStream >> baseFrame._headRotation.x >> baseFrame._headRotation.y >> baseFrame._headRotation.z >> baseFrame._headRotation.w;
|
||||||
|
fileStream >> baseFrame._leanSideways;
|
||||||
|
fileStream >> baseFrame._leanForward;
|
||||||
|
|
||||||
|
recording->_frames << baseFrame;
|
||||||
|
|
||||||
|
for (int i = 1; i < recording->_timestamps.size(); ++i) {
|
||||||
|
QBitArray mask;
|
||||||
|
QByteArray buffer;
|
||||||
|
QDataStream stream(&buffer, QIODevice::ReadOnly);
|
||||||
|
RecordingFrame frame;
|
||||||
|
RecordingFrame& previousFrame = recording->_frames.last();
|
||||||
|
|
||||||
|
fileStream >> mask;
|
||||||
|
fileStream >> buffer;
|
||||||
|
int maskIndex = 0;
|
||||||
|
|
||||||
|
// Blendshape Coefficients
|
||||||
|
frame._blendshapeCoefficients.resize(baseFrame._blendshapeCoefficients.size());
|
||||||
|
for (int i = 0; i < baseFrame._blendshapeCoefficients.size(); ++i) {
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._blendshapeCoefficients[i];
|
||||||
|
} else {
|
||||||
|
frame._blendshapeCoefficients[i] = previousFrame._blendshapeCoefficients[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joint Rotations
|
||||||
|
frame._jointRotations.resize(baseFrame._jointRotations.size());
|
||||||
|
for (int i = 0; i < baseFrame._jointRotations.size(); ++i) {
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._jointRotations[i].x >> frame._jointRotations[i].y >> frame._jointRotations[i].z >> frame._jointRotations[i].w;
|
||||||
|
} else {
|
||||||
|
frame._jointRotations[i] = previousFrame._jointRotations[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._translation.x >> frame._translation.y >> frame._translation.z;
|
||||||
|
} else {
|
||||||
|
frame._translation = previousFrame._translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._rotation.x >> frame._rotation.y >> frame._rotation.z >> frame._rotation.w;
|
||||||
|
} else {
|
||||||
|
frame._rotation = previousFrame._rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._scale;
|
||||||
|
} else {
|
||||||
|
frame._scale = previousFrame._scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._headRotation.x >> frame._headRotation.y >> frame._headRotation.z >> frame._headRotation.w;
|
||||||
|
} else {
|
||||||
|
frame._headRotation = previousFrame._headRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._leanSideways;
|
||||||
|
} else {
|
||||||
|
frame._leanSideways = previousFrame._leanSideways;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask[maskIndex++]) {
|
||||||
|
stream >> frame._leanForward;
|
||||||
|
} else {
|
||||||
|
frame._leanForward = previousFrame._leanForward;
|
||||||
|
}
|
||||||
|
|
||||||
|
recording->_frames << frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray audioArray;
|
||||||
|
fileStream >> audioArray;
|
||||||
|
recording->addAudioPacket(audioArray);
|
||||||
|
|
||||||
|
|
||||||
|
qDebug() << "Read " << file.size() << " bytes";
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
47
interface/src/SpeechRecognizer.h
Normal file
47
interface/src/SpeechRecognizer.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// SpeechRecognizer.h
|
||||||
|
// interface/src
|
||||||
|
//
|
||||||
|
// Created by Ryan Huffman on 07/31/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_SpeechRecognizer_h
|
||||||
|
#define hifi_SpeechRecognizer_h
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class SpeechRecognizer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SpeechRecognizer();
|
||||||
|
~SpeechRecognizer();
|
||||||
|
|
||||||
|
void handleCommandRecognized(const char* command);
|
||||||
|
bool getEnabled() const { return _enabled; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setEnabled(bool enabled);
|
||||||
|
void addCommand(const QString& command);
|
||||||
|
void removeCommand(const QString& command);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void commandRecognized(const QString& command);
|
||||||
|
void enabledUpdated(bool enabled);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void reloadCommands();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _enabled;
|
||||||
|
QSet<QString> _commands;
|
||||||
|
void* _speechRecognizerDelegate;
|
||||||
|
void* _speechRecognizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_SpeechRecognizer_h
|
109
interface/src/SpeechRecognizer.mm
Normal file
109
interface/src/SpeechRecognizer.mm
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// SpeechRecognizer.mm
|
||||||
|
// interface/src
|
||||||
|
//
|
||||||
|
// Created by Ryan Huffman on 07/31/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AppKit/NSSpeechRecognizer.h>
|
||||||
|
#import <AppKit/NSWorkspace.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "SpeechRecognizer.h"
|
||||||
|
|
||||||
|
@interface SpeechRecognizerDelegate : NSObject <NSSpeechRecognizerDelegate> {
|
||||||
|
SpeechRecognizer* _listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setListener:(SpeechRecognizer*)listener;
|
||||||
|
- (void)speechRecognizer:(NSSpeechRecognizer*)sender didRecognizeCommand:(id)command;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SpeechRecognizerDelegate
|
||||||
|
|
||||||
|
- (void)setListener:(SpeechRecognizer*)listener {
|
||||||
|
_listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)speechRecognizer:(NSSpeechRecognizer*)sender didRecognizeCommand:(id)command {
|
||||||
|
_listener->handleCommandRecognized(((NSString*)command).UTF8String);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
SpeechRecognizer::SpeechRecognizer() :
|
||||||
|
QObject(),
|
||||||
|
_enabled(false),
|
||||||
|
_commands(),
|
||||||
|
_speechRecognizerDelegate([[SpeechRecognizerDelegate alloc] init]),
|
||||||
|
_speechRecognizer(NULL) {
|
||||||
|
|
||||||
|
[(id)_speechRecognizerDelegate setListener:this];
|
||||||
|
}
|
||||||
|
|
||||||
|
SpeechRecognizer::~SpeechRecognizer() {
|
||||||
|
if (_speechRecognizer) {
|
||||||
|
[(id)_speechRecognizer dealloc];
|
||||||
|
}
|
||||||
|
if (_speechRecognizerDelegate) {
|
||||||
|
[(id)_speechRecognizerDelegate dealloc];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeechRecognizer::handleCommandRecognized(const char* command) {
|
||||||
|
emit commandRecognized(QString(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeechRecognizer::setEnabled(bool enabled) {
|
||||||
|
if (enabled == _enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_enabled = enabled;
|
||||||
|
if (_enabled) {
|
||||||
|
_speechRecognizer = [[NSSpeechRecognizer alloc] init];
|
||||||
|
|
||||||
|
reloadCommands();
|
||||||
|
|
||||||
|
[(id)_speechRecognizer setDelegate:(id)_speechRecognizerDelegate];
|
||||||
|
[(id)_speechRecognizer startListening];
|
||||||
|
} else {
|
||||||
|
[(id)_speechRecognizer stopListening];
|
||||||
|
[(id)_speechRecognizer dealloc];
|
||||||
|
_speechRecognizer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit enabledUpdated(_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeechRecognizer::reloadCommands() {
|
||||||
|
if (_speechRecognizer) {
|
||||||
|
NSMutableArray* cmds = [NSMutableArray array];
|
||||||
|
for (QSet<QString>::const_iterator iter = _commands.constBegin(); iter != _commands.constEnd(); iter++) {
|
||||||
|
[cmds addObject:[NSString stringWithUTF8String:(*iter).toLocal8Bit().data()]];
|
||||||
|
}
|
||||||
|
[(id)_speechRecognizer setCommands:cmds];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeechRecognizer::addCommand(const QString& command) {
|
||||||
|
_commands.insert(command);
|
||||||
|
reloadCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeechRecognizer::removeCommand(const QString& command) {
|
||||||
|
_commands.remove(command);
|
||||||
|
reloadCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Q_OS_MAC
|
|
@ -91,7 +91,7 @@ public:
|
||||||
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||||
glm::vec3 getChestPosition() const;
|
glm::vec3 getChestPosition() const;
|
||||||
float getScale() const { return _scale; }
|
float getScale() const { return _scale; }
|
||||||
const glm::vec3& getVelocity() const { return _velocity; }
|
Q_INVOKABLE const glm::vec3& getVelocity() const { return _velocity; }
|
||||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
||||||
|
@ -152,9 +152,9 @@ public:
|
||||||
Q_INVOKABLE glm::quat getJointCombinedRotation(int index) const;
|
Q_INVOKABLE glm::quat getJointCombinedRotation(int index) const;
|
||||||
Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const;
|
Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const;
|
||||||
|
|
||||||
glm::vec3 getAcceleration() const { return _acceleration; }
|
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
|
||||||
glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||||
glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||||
|
|
||||||
|
|
||||||
/// Scales a world space position vector relative to the avatar position and scale
|
/// Scales a world space position vector relative to the avatar position and scale
|
||||||
|
|
|
@ -508,40 +508,105 @@ bool MyAvatar::setJointReferential(int id, int jointIndex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::isRecording() const {
|
bool MyAvatar::isRecording() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
bool result;
|
||||||
|
QMetaObject::invokeMethod(this, "isRecording", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
return _recorder && _recorder->isRecording();
|
return _recorder && _recorder->isRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
RecorderPointer MyAvatar::startRecording() {
|
void MyAvatar::startRecording() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!_recorder) {
|
if (!_recorder) {
|
||||||
_recorder = RecorderPointer(new Recorder(this));
|
_recorder = RecorderPointer(new Recorder(this));
|
||||||
}
|
}
|
||||||
|
Application::getInstance()->getAudio()->setRecorder(_recorder);
|
||||||
_recorder->startRecording();
|
_recorder->startRecording();
|
||||||
return _recorder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::stopRecording() {
|
void MyAvatar::stopRecording() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "stopRecording", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (_recorder) {
|
if (_recorder) {
|
||||||
_recorder->stopRecording();
|
_recorder->stopRecording();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::isPlaying() const {
|
void MyAvatar::saveRecording(QString filename) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QString, filename));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_recorder) {
|
||||||
|
_recorder->saveToFile(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::isPlaying() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
bool result;
|
||||||
|
QMetaObject::invokeMethod(this, "isPlaying", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
return _player && _player->isPlaying();
|
return _player && _player->isPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerPointer MyAvatar::startPlaying() {
|
void MyAvatar::loadRecording(QString filename) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QString, filename));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!_player) {
|
if (!_player) {
|
||||||
_player = PlayerPointer(new Player(this));
|
_player = PlayerPointer(new Player(this));
|
||||||
}
|
}
|
||||||
if (_recorder) {
|
|
||||||
_player->loadRecording(_recorder->getRecording());
|
_player->loadFromFile(filename);
|
||||||
_player->startPlaying();
|
}
|
||||||
|
|
||||||
|
void MyAvatar::loadLastRecording() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return _player;
|
if (!_recorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_player) {
|
||||||
|
_player = PlayerPointer(new Player(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
_player->loadRecording(_recorder->getRecording());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::startPlaying() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_player) {
|
||||||
|
_player = PlayerPointer(new Player(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::getInstance()->getAudio()->setPlayer(_player);
|
||||||
|
_player->startPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::stopPlaying() {
|
void MyAvatar::stopPlaying() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->stopPlaying();
|
_player->stopPlaying();
|
||||||
}
|
}
|
||||||
|
@ -955,7 +1020,7 @@ void MyAvatar::clearJointsData() {
|
||||||
for (int i = 0; i < _jointData.size(); ++i) {
|
for (int i = 0; i < _jointData.size(); ++i) {
|
||||||
Avatar::clearJointData(i);
|
Avatar::clearJointData(i);
|
||||||
if (QThread::currentThread() == thread()) {
|
if (QThread::currentThread() == thread()) {
|
||||||
_skeletonModel.clearJointState(i);
|
_skeletonModel.clearJointAnimationPriority(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,15 @@ public:
|
||||||
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
||||||
|
|
||||||
static void sendKillAvatar();
|
static void sendKillAvatar();
|
||||||
|
|
||||||
|
Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); }
|
||||||
|
Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); }
|
||||||
|
Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); }
|
||||||
|
Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); }
|
||||||
|
Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); }
|
||||||
|
|
||||||
|
Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); }
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
||||||
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||||
void updateLookAtTargetAvatar();
|
void updateLookAtTargetAvatar();
|
||||||
|
@ -134,6 +142,10 @@ public:
|
||||||
/// Renders a laser pointer for UI picking
|
/// Renders a laser pointer for UI picking
|
||||||
void renderLaserPointers();
|
void renderLaserPointers();
|
||||||
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
|
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
|
||||||
|
|
||||||
|
const RecorderPointer getRecorder() const { return _recorder; }
|
||||||
|
const PlayerPointer getPlayer() const { return _player; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void goHome();
|
void goHome();
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
|
@ -157,14 +169,15 @@ public slots:
|
||||||
bool setModelReferential(int id);
|
bool setModelReferential(int id);
|
||||||
bool setJointReferential(int id, int jointIndex);
|
bool setJointReferential(int id, int jointIndex);
|
||||||
|
|
||||||
const RecorderPointer getRecorder() const { return _recorder; }
|
bool isRecording();
|
||||||
bool isRecording() const;
|
void startRecording();
|
||||||
RecorderPointer startRecording();
|
|
||||||
void stopRecording();
|
void stopRecording();
|
||||||
|
void saveRecording(QString filename);
|
||||||
|
|
||||||
const PlayerPointer getPlayer() const { return _player; }
|
bool isPlaying();
|
||||||
bool isPlaying() const;
|
void loadRecording(QString filename);
|
||||||
PlayerPointer startPlaying();
|
void loadLastRecording();
|
||||||
|
void startPlaying();
|
||||||
void stopPlaying();
|
void stopPlaying();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -389,7 +389,6 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||||
if (theta > EPSILON) {
|
if (theta > EPSILON) {
|
||||||
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
||||||
const float AVERAGE_CARA_FRAME_TIME = 0.04f;
|
const float AVERAGE_CARA_FRAME_TIME = 0.04f;
|
||||||
const float ANGULAR_VELOCITY_MIN = 1.2f;
|
|
||||||
const float YAW_STANDARD_DEV_DEG = 2.5f;
|
const float YAW_STANDARD_DEV_DEG = 2.5f;
|
||||||
|
|
||||||
_headAngularVelocity = theta / AVERAGE_CARA_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag;
|
_headAngularVelocity = theta / AVERAGE_CARA_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag;
|
||||||
|
|
|
@ -194,12 +194,12 @@ float updateAndGetCoefficient(float * coefficient, float currentValue, bool scal
|
||||||
coefficient[AVG] = LONG_TERM_AVERAGE * coefficient[AVG] + (1.f - LONG_TERM_AVERAGE) * currentValue;
|
coefficient[AVG] = LONG_TERM_AVERAGE * coefficient[AVG] + (1.f - LONG_TERM_AVERAGE) * currentValue;
|
||||||
if (coefficient[MAX] > coefficient[MIN]) {
|
if (coefficient[MAX] > coefficient[MIN]) {
|
||||||
if (scaleToRange) {
|
if (scaleToRange) {
|
||||||
return glm::clamp((currentValue - coefficient[AVG]) / (coefficient[MAX] - coefficient[MIN]), 0.f, 1.f);
|
return glm::clamp((currentValue - coefficient[AVG]) / (coefficient[MAX] - coefficient[MIN]), 0.0f, 1.0f);
|
||||||
} else {
|
} else {
|
||||||
return glm::clamp(currentValue - coefficient[AVG], 0.f, 1.f);
|
return glm::clamp(currentValue - coefficient[AVG], 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0.f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,13 +242,11 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||||
|
|
||||||
// Set blendshapes
|
// Set blendshapes
|
||||||
float EYE_MAGNIFIER = 4.0f;
|
float EYE_MAGNIFIER = 4.0f;
|
||||||
|
float rightEye = glm::clamp((updateAndGetCoefficient(_rightEye, packet.expressions[0])) * EYE_MAGNIFIER, 0.0f, 1.0f);
|
||||||
float rightEye = (updateAndGetCoefficient(_rightEye, packet.expressions[0])) * EYE_MAGNIFIER;
|
|
||||||
_blendshapeCoefficients[_rightBlinkIndex] = rightEye;
|
_blendshapeCoefficients[_rightBlinkIndex] = rightEye;
|
||||||
float leftEye = (updateAndGetCoefficient(_leftEye, packet.expressions[1])) * EYE_MAGNIFIER;
|
float leftEye = glm::clamp((updateAndGetCoefficient(_leftEye, packet.expressions[1])) * EYE_MAGNIFIER, 0.0f, 1.0f);
|
||||||
_blendshapeCoefficients[_leftBlinkIndex] = leftEye;
|
_blendshapeCoefficients[_leftBlinkIndex] = leftEye;
|
||||||
|
|
||||||
// Right eye = packet.expressions[0];
|
|
||||||
|
|
||||||
float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]);
|
float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]);
|
||||||
if (leftBrow < 0.5f) {
|
if (leftBrow < 0.5f) {
|
||||||
|
@ -270,9 +268,9 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||||
float JAW_OPEN_MAGNIFIER = 1.4f;
|
float JAW_OPEN_MAGNIFIER = 1.4f;
|
||||||
_blendshapeCoefficients[_jawOpenIndex] = rescaleCoef(packet.expressions[21]) * JAW_OPEN_MAGNIFIER;
|
_blendshapeCoefficients[_jawOpenIndex] = rescaleCoef(packet.expressions[21]) * JAW_OPEN_MAGNIFIER;
|
||||||
|
|
||||||
|
float SMILE_MULTIPLIER = 2.0f;
|
||||||
_blendshapeCoefficients[_mouthSmileLeftIndex] = rescaleCoef(packet.expressions[24]);
|
_blendshapeCoefficients[_mouthSmileLeftIndex] = glm::clamp(packet.expressions[24] * SMILE_MULTIPLIER, 0.0f, 1.0f);
|
||||||
_blendshapeCoefficients[_mouthSmileRightIndex] = rescaleCoef(packet.expressions[23]);
|
_blendshapeCoefficients[_mouthSmileRightIndex] = glm::clamp(packet.expressions[23] * SMILE_MULTIPLIER, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,11 +26,17 @@ using namespace fs;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const quint16 FACESHIFT_PORT = 33433;
|
const quint16 FACESHIFT_PORT = 33433;
|
||||||
|
float STARTING_FACESHIFT_FRAME_TIME = 0.033f;
|
||||||
|
|
||||||
Faceshift::Faceshift() :
|
Faceshift::Faceshift() :
|
||||||
_tcpEnabled(true),
|
_tcpEnabled(true),
|
||||||
_tcpRetryCount(0),
|
_tcpRetryCount(0),
|
||||||
_lastTrackingStateReceived(0),
|
_lastTrackingStateReceived(0),
|
||||||
|
_averageFrameTime(STARTING_FACESHIFT_FRAME_TIME),
|
||||||
|
_headAngularVelocity(0),
|
||||||
|
_headLinearVelocity(0),
|
||||||
|
_lastHeadTranslation(0),
|
||||||
|
_filteredHeadTranslation(0),
|
||||||
_eyeGazeLeftPitch(0.0f),
|
_eyeGazeLeftPitch(0.0f),
|
||||||
_eyeGazeLeftYaw(0.0f),
|
_eyeGazeLeftYaw(0.0f),
|
||||||
_eyeGazeRightPitch(0.0f),
|
_eyeGazeRightPitch(0.0f),
|
||||||
|
@ -209,23 +215,41 @@ void Faceshift::receive(const QByteArray& buffer) {
|
||||||
float theta = 2 * acos(r.w);
|
float theta = 2 * acos(r.w);
|
||||||
if (theta > EPSILON) {
|
if (theta > EPSILON) {
|
||||||
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
||||||
float AVERAGE_FACESHIFT_FRAME_TIME = 0.033f;
|
_headAngularVelocity = theta / _averageFrameTime * glm::vec3(r.x, r.y, r.z) / rMag;
|
||||||
_headAngularVelocity = theta / AVERAGE_FACESHIFT_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag;
|
|
||||||
} else {
|
} else {
|
||||||
_headAngularVelocity = glm::vec3(0,0,0);
|
_headAngularVelocity = glm::vec3(0,0,0);
|
||||||
}
|
}
|
||||||
_headRotation = newRotation;
|
const float ANGULAR_VELOCITY_FILTER_STRENGTH = 0.3f;
|
||||||
|
_headRotation = safeMix(_headRotation, newRotation, glm::clamp(glm::length(_headAngularVelocity) *
|
||||||
|
ANGULAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f));
|
||||||
|
|
||||||
const float TRANSLATION_SCALE = 0.02f;
|
const float TRANSLATION_SCALE = 0.02f;
|
||||||
_headTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y,
|
glm::vec3 newHeadTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y,
|
||||||
-data.m_headTranslation.z) * TRANSLATION_SCALE;
|
-data.m_headTranslation.z) * TRANSLATION_SCALE;
|
||||||
|
|
||||||
|
_headLinearVelocity = (newHeadTranslation - _lastHeadTranslation) / _averageFrameTime;
|
||||||
|
|
||||||
|
const float LINEAR_VELOCITY_FILTER_STRENGTH = 0.3f;
|
||||||
|
float velocityFilter = glm::clamp(1.0f - glm::length(_headLinearVelocity) *
|
||||||
|
LINEAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
|
||||||
|
_filteredHeadTranslation = velocityFilter * _filteredHeadTranslation + (1.0f - velocityFilter) * newHeadTranslation;
|
||||||
|
|
||||||
|
_lastHeadTranslation = newHeadTranslation;
|
||||||
|
_headTranslation = _filteredHeadTranslation;
|
||||||
|
|
||||||
_eyeGazeLeftPitch = -data.m_eyeGazeLeftPitch;
|
_eyeGazeLeftPitch = -data.m_eyeGazeLeftPitch;
|
||||||
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
|
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
|
||||||
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
|
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
|
||||||
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
|
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
|
||||||
_blendshapeCoefficients = QVector<float>::fromStdVector(data.m_coeffs);
|
_blendshapeCoefficients = QVector<float>::fromStdVector(data.m_coeffs);
|
||||||
|
|
||||||
_lastTrackingStateReceived = usecTimestampNow();
|
const float FRAME_AVERAGING_FACTOR = 0.99f;
|
||||||
|
quint64 usecsNow = usecTimestampNow();
|
||||||
|
if (_lastTrackingStateReceived != 0) {
|
||||||
|
_averageFrameTime = FRAME_AVERAGING_FACTOR * _averageFrameTime +
|
||||||
|
(1.0f - FRAME_AVERAGING_FACTOR) * (float)(usecsNow - _lastTrackingStateReceived) / 1000000.0f;
|
||||||
|
}
|
||||||
|
_lastTrackingStateReceived = usecsNow;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,8 +98,12 @@ private:
|
||||||
int _tcpRetryCount;
|
int _tcpRetryCount;
|
||||||
bool _tracking;
|
bool _tracking;
|
||||||
quint64 _lastTrackingStateReceived;
|
quint64 _lastTrackingStateReceived;
|
||||||
|
float _averageFrameTime;
|
||||||
|
|
||||||
glm::vec3 _headAngularVelocity;
|
glm::vec3 _headAngularVelocity;
|
||||||
|
glm::vec3 _headLinearVelocity;
|
||||||
|
glm::vec3 _lastHeadTranslation;
|
||||||
|
glm::vec3 _filteredHeadTranslation;
|
||||||
|
|
||||||
// degrees
|
// degrees
|
||||||
float _eyeGazeLeftPitch;
|
float _eyeGazeLeftPitch;
|
||||||
|
|
|
@ -700,6 +700,12 @@ void Model::clearJointState(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::clearJointAnimationPriority(int index) {
|
||||||
|
if (index != -1 && index < _jointStates.size()) {
|
||||||
|
_jointStates[index]._animationPriority = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
if (index != -1 && index < _jointStates.size()) {
|
||||||
JointState& state = _jointStates[index];
|
JointState& state = _jointStates[index];
|
||||||
|
|
|
@ -121,6 +121,9 @@ public:
|
||||||
/// Clear the joint states
|
/// Clear the joint states
|
||||||
void clearJointState(int index);
|
void clearJointState(int index);
|
||||||
|
|
||||||
|
/// Clear the joint animation priority
|
||||||
|
void clearJointAnimationPriority(int index);
|
||||||
|
|
||||||
/// Sets the joint state at the specified index.
|
/// Sets the joint state at the specified index.
|
||||||
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
|
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
|
||||||
|
|
||||||
|
|
|
@ -282,19 +282,10 @@ void Stats::display(
|
||||||
pingVoxel = totalPingVoxel/voxelServerCount;
|
pingVoxel = totalPingVoxel/voxelServerCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Audio* audio = Application::getInstance()->getAudio();
|
|
||||||
|
|
||||||
lines = _expanded ? 4 : 3;
|
lines = _expanded ? 4 : 3;
|
||||||
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||||
horizontalOffset += 5;
|
horizontalOffset += 5;
|
||||||
|
|
||||||
char audioJitter[30];
|
|
||||||
sprintf(audioJitter,
|
|
||||||
"Buffer msecs %.1f",
|
|
||||||
audio->getDesiredJitterBufferFrames() * BUFFER_SEND_INTERVAL_USECS / (float)USECS_PER_MSEC);
|
|
||||||
drawText(30, glWidget->height() - 22, scale, rotation, font, audioJitter, color);
|
|
||||||
|
|
||||||
|
|
||||||
char audioPing[30];
|
char audioPing[30];
|
||||||
sprintf(audioPing, "Audio ping: %d", pingAudio);
|
sprintf(audioPing, "Audio ping: %d", pingAudio);
|
||||||
|
@ -698,27 +689,6 @@ void Stats::display(
|
||||||
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
|
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw local light stats
|
|
||||||
QVector<Model::LocalLight> localLights = Application::getInstance()->getAvatarManager().getLocalLights();
|
|
||||||
verticalOffset = 400;
|
|
||||||
horizontalOffset = 20;
|
|
||||||
|
|
||||||
char buffer[128];
|
|
||||||
for (int i = 0; i < localLights.size(); i++) {
|
|
||||||
glm::vec3 lightDirection = localLights.at(i).direction;
|
|
||||||
snprintf(buffer, sizeof(buffer), "Light %d direction (%.2f, %.2f, %.2f)", i, lightDirection.x, lightDirection.y, lightDirection.z);
|
|
||||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, buffer, color);
|
|
||||||
|
|
||||||
verticalOffset += STATS_PELS_PER_LINE;
|
|
||||||
|
|
||||||
glm::vec3 lightColor = localLights.at(i).color;
|
|
||||||
snprintf(buffer, sizeof(buffer), "Light %d color (%.2f, %.2f, %.2f)", i, lightColor.x, lightColor.y, lightColor.z);
|
|
||||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, buffer, color);
|
|
||||||
|
|
||||||
verticalOffset += STATS_PELS_PER_LINE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -687,7 +687,7 @@ QVector<glm::quat> AvatarData::getJointRotations() const {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QVector<glm::quat> result;
|
QVector<glm::quat> result;
|
||||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
||||||
"getJointRotation", Qt::BlockingQueuedConnection,
|
"getJointRotations", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(QVector<glm::quat>, result));
|
Q_RETURN_ARG(QVector<glm::quat>, result));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -702,7 +702,7 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QVector<glm::quat> result;
|
QVector<glm::quat> result;
|
||||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
||||||
"setJointRotation", Qt::BlockingQueuedConnection,
|
"setJointRotations", Qt::BlockingQueuedConnection,
|
||||||
Q_ARG(QVector<glm::quat>, jointRotations));
|
Q_ARG(QVector<glm::quat>, jointRotations));
|
||||||
}
|
}
|
||||||
for (int i = 0; i < jointRotations.size(); ++i) {
|
for (int i = 0; i < jointRotations.size(); ++i) {
|
||||||
|
|
|
@ -78,6 +78,8 @@ KeyEvent::KeyEvent(const QKeyEvent& event) {
|
||||||
text = "LEFT";
|
text = "LEFT";
|
||||||
} else if (key == Qt::Key_Right) {
|
} else if (key == Qt::Key_Right) {
|
||||||
text = "RIGHT";
|
text = "RIGHT";
|
||||||
|
} else if (key == Qt::Key_Space) {
|
||||||
|
text = "SPACE";
|
||||||
} else if (key == Qt::Key_Escape) {
|
} else if (key == Qt::Key_Escape) {
|
||||||
text = "ESC";
|
text = "ESC";
|
||||||
} else if (key == Qt::Key_Tab) {
|
} else if (key == Qt::Key_Tab) {
|
||||||
|
@ -220,6 +222,8 @@ void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
|
||||||
} else if (event.text.toUpper() == "RIGHT") {
|
} else if (event.text.toUpper() == "RIGHT") {
|
||||||
event.key = Qt::Key_Right;
|
event.key = Qt::Key_Right;
|
||||||
event.isKeypad = true;
|
event.isKeypad = true;
|
||||||
|
} else if (event.text.toUpper() == "SPACE") {
|
||||||
|
event.key = Qt::Key_Space;
|
||||||
} else if (event.text.toUpper() == "ESC") {
|
} else if (event.text.toUpper() == "ESC") {
|
||||||
event.key = Qt::Key_Escape;
|
event.key = Qt::Key_Escape;
|
||||||
} else if (event.text.toUpper() == "TAB") {
|
} else if (event.text.toUpper() == "TAB") {
|
||||||
|
|
Loading…
Reference in a new issue