mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 06:44:06 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels
This commit is contained in:
commit
530f5b0df8
36 changed files with 1896 additions and 349 deletions
203
examples/Recorder.js
Normal file
203
examples/Recorder.js
Normal file
|
@ -0,0 +1,203 @@
|
|||
//
|
||||
// Recorder.js
|
||||
// examples
|
||||
//
|
||||
// Created by Clément Brisset on 8/20/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
|
||||
//
|
||||
|
||||
Script.include("toolBars.js");
|
||||
|
||||
var recordingFile = "recording.rec";
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/";
|
||||
var ALPHA_ON = 1.0;
|
||||
var ALPHA_OFF = 0.7;
|
||||
var COLOR_ON = { red: 128, green: 0, blue: 0 };
|
||||
var COLOR_OFF = { red: 128, green: 128, blue: 128 };
|
||||
Tool.IMAGE_WIDTH *= 0.7;
|
||||
Tool.IMAGE_HEIGHT *= 0.7;
|
||||
|
||||
var toolBar = null;
|
||||
var recordIcon;
|
||||
var playIcon;
|
||||
var saveIcon;
|
||||
var loadIcon;
|
||||
setupToolBar();
|
||||
|
||||
var timer = null;
|
||||
setupTimer();
|
||||
|
||||
function setupToolBar() {
|
||||
if (toolBar != null) {
|
||||
print("Multiple calls to Recorder.js:setupToolBar()");
|
||||
return;
|
||||
}
|
||||
|
||||
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||
toolBar.setBack(COLOR_OFF, ALPHA_OFF);
|
||||
|
||||
recordIcon = toolBar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "record.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_ON,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
playIcon = toolBar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "play.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_ON,
|
||||
visible: true
|
||||
}, false, false);
|
||||
|
||||
saveIcon = toolBar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "save.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_ON,
|
||||
visible: true
|
||||
}, false, false);
|
||||
|
||||
loadIcon = toolBar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "load.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_ON,
|
||||
visible: true
|
||||
}, false, false);
|
||||
}
|
||||
|
||||
function setupTimer() {
|
||||
timer = Overlays.addOverlay("text", {
|
||||
font: { size: 20 },
|
||||
text: (0.00).toFixed(3),
|
||||
backgroundColor: COLOR_OFF,
|
||||
x: 0, y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
alpha: 1.0,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
|
||||
function updateTimer() {
|
||||
var text = "";
|
||||
if (MyAvatar.isRecording()) {
|
||||
text = formatTime(MyAvatar.recorderElapsed())
|
||||
} else {
|
||||
text = formatTime(MyAvatar.playerElapsed()) + " / " +
|
||||
formatTime(MyAvatar.playerLength());
|
||||
}
|
||||
|
||||
Overlays.editOverlay(timer, {
|
||||
text: text
|
||||
})
|
||||
}
|
||||
|
||||
function formatTime(time) {
|
||||
var MIN_PER_HOUR = 60;
|
||||
var SEC_PER_MIN = 60;
|
||||
var MSEC_PER_SEC = 1000;
|
||||
|
||||
var hours = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR));
|
||||
time -= hours * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR);
|
||||
|
||||
var minutes = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN));
|
||||
time -= minutes * (MSEC_PER_SEC * SEC_PER_MIN);
|
||||
|
||||
var seconds = Math.floor(time / MSEC_PER_SEC);
|
||||
seconds = time / MSEC_PER_SEC;
|
||||
|
||||
var text = "";
|
||||
text += (hours > 0) ? hours + ":" :
|
||||
"";
|
||||
text += (minutes > 0) ? ((minutes < 10 && text != "") ? "0" : "") + minutes + ":" :
|
||||
"";
|
||||
text += ((seconds < 10 && text != "") ? "0" : "") + seconds.toFixed(3);
|
||||
return text;
|
||||
}
|
||||
|
||||
function moveUI() {
|
||||
var relative = { x: 30, y: 90 };
|
||||
toolBar.move(relative.x,
|
||||
windowDimensions.y - relative.y);
|
||||
Overlays.editOverlay(timer, {
|
||||
x: relative.x - 10,
|
||||
y: windowDimensions.y - relative.y - 35,
|
||||
width: 0,
|
||||
height: 0
|
||||
});
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
|
||||
if (recordIcon === toolBar.clicked(clickedOverlay)) {
|
||||
if (!MyAvatar.isRecording()) {
|
||||
MyAvatar.startRecording();
|
||||
toolBar.setBack(COLOR_ON, ALPHA_ON);
|
||||
} else {
|
||||
MyAvatar.stopRecording();
|
||||
MyAvatar.loadLastRecording();
|
||||
toolBar.setBack(COLOR_OFF, ALPHA_OFF);
|
||||
}
|
||||
} else if (playIcon === toolBar.clicked(clickedOverlay)) {
|
||||
if (!MyAvatar.isRecording()) {
|
||||
if (MyAvatar.isPlaying()) {
|
||||
MyAvatar.stopPlaying();
|
||||
} else {
|
||||
MyAvatar.startPlaying();
|
||||
}
|
||||
}
|
||||
} else if (saveIcon === toolBar.clicked(clickedOverlay)) {
|
||||
if (!MyAvatar.isRecording()) {
|
||||
recordingFile = Window.save("Save recording to file", ".", "*.rec");
|
||||
MyAvatar.saveRecording(recordingFile);
|
||||
}
|
||||
} else if (loadIcon === toolBar.clicked(clickedOverlay)) {
|
||||
if (!MyAvatar.isRecording()) {
|
||||
recordingFile = Window.browse("Load recorcding from file", ".", "*.rec");
|
||||
MyAvatar.loadRecording(recordingFile);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
var newDimensions = Controller.getViewportDimensions();
|
||||
if (windowDimensions.x != newDimensions.x ||
|
||||
windowDimensions.y != newDimensions.y) {
|
||||
windowDimensions = newDimensions;
|
||||
moveUI();
|
||||
}
|
||||
|
||||
updateTimer();
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
if (MyAvatar.isRecording()) {
|
||||
MyAvatar.stopRecording();
|
||||
}
|
||||
if (MyAvatar.isPlaying()) {
|
||||
MyAvatar.stopPlaying();
|
||||
}
|
||||
toolBar.cleanup();
|
||||
Overlays.deleteOverlay(timer);
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
// Should be called last to put everything into position
|
||||
moveUI();
|
||||
|
||||
|
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 THRUST_CONTROLLER = 0;
|
||||
var VIEW_CONTROLLER = 1;
|
||||
var INITIAL_THRUST_MULTPLIER = 1.0;
|
||||
var INITIAL_THRUST_MULTIPLIER = 1.0;
|
||||
var THRUST_INCREASE_RATE = 1.05;
|
||||
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 grabStartPosition = { 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 grabbingWithLeftHand = false;
|
||||
var wasGrabbingWithLeftHand = false;
|
||||
var movingWithHead = false;
|
||||
var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw;
|
||||
var EPSILON = 0.000001;
|
||||
var velocity = { x: 0, y: 0, z: 0};
|
||||
var THRUST_MAG_UP = 100.0;
|
||||
|
@ -241,6 +243,47 @@ function handleGrabBehavior(deltaTime) {
|
|||
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
|
||||
function flyWithHydra(deltaTime) {
|
||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||
|
@ -262,7 +305,7 @@ function flyWithHydra(deltaTime) {
|
|||
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustRight);
|
||||
} else {
|
||||
thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
||||
thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
|
||||
}
|
||||
|
||||
// View Controller
|
||||
|
@ -280,6 +323,7 @@ function flyWithHydra(deltaTime) {
|
|||
MyAvatar.headPitch = newPitch;
|
||||
}
|
||||
handleGrabBehavior(deltaTime);
|
||||
moveWithHead(deltaTime);
|
||||
displayDebug();
|
||||
|
||||
}
|
||||
|
@ -296,3 +340,19 @@ function 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();
|
|
@ -132,20 +132,34 @@ ToolBar = function(x, y, direction) {
|
|||
this.y = y;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
this.back = this.back = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
alpha: 1.0,
|
||||
visible: false
|
||||
});
|
||||
|
||||
this.addTool = function(properties, selectable, selected) {
|
||||
if (direction == ToolBar.HORIZONTAL) {
|
||||
properties.x = this.x + this.width;
|
||||
properties.y = this.y;
|
||||
this.width += properties.width + ToolBar.SPACING;
|
||||
this.height += Math.max(properties.height, this.height);
|
||||
this.height = Math.max(properties.height, this.height);
|
||||
} else {
|
||||
properties.x = this.x;
|
||||
properties.y = this.y + this.height;
|
||||
this.width = Math.max(properties.width, this.width);
|
||||
this.height += properties.height + ToolBar.SPACING;
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
width: this.width + 2 * ToolBar.SPACING,
|
||||
height: this.height + 2 * ToolBar.SPACING
|
||||
});
|
||||
}
|
||||
|
||||
this.tools[this.tools.length] = new Tool(properties, selectable, selected);
|
||||
return ((this.tools.length) - 1);
|
||||
|
@ -159,18 +173,48 @@ ToolBar = function(x, y, direction) {
|
|||
for(var tool in this.tools) {
|
||||
this.tools[tool].move(this.tools[tool].x() + dx, this.tools[tool].y() + dy);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
x: x - ToolBar.SPACING,
|
||||
y: y - ToolBar.SPACING
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.setAlpha = function(alpha) {
|
||||
for(var tool in this.tools) {
|
||||
this.setAlpha = function(alpha, tool) {
|
||||
if(typeof(tool) === 'undefined') {
|
||||
for(var tool in this.tools) {
|
||||
this.tools[tool].setAlpha(alpha);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, { alpha: alpha});
|
||||
}
|
||||
} else {
|
||||
this.tools[tool].setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
this.setBack = function(color, alpha) {
|
||||
if (color == null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
visible: false
|
||||
});
|
||||
} else {
|
||||
Overlays.editOverlay(this.back, {
|
||||
visible: true,
|
||||
backgroundColor: color,
|
||||
alpha: alpha
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.show = function(doShow) {
|
||||
for(var tool in this.tools) {
|
||||
this.tools[tool].show(doShow);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, { visible: doShow});
|
||||
}
|
||||
}
|
||||
|
||||
this.clicked = function(clickedOverlay) {
|
||||
|
@ -200,6 +244,11 @@ ToolBar = function(x, y, direction) {
|
|||
delete this.tools[tool];
|
||||
}
|
||||
|
||||
if (this.back != null) {
|
||||
Overlays.deleteOverlay(this.back);
|
||||
this.back = null;
|
||||
}
|
||||
|
||||
this.tools = [];
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
|
|
@ -46,6 +46,15 @@ foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels pa
|
|||
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}")
|
||||
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)
|
||||
|
||||
# grab the ui files in resources/ui
|
||||
|
@ -165,8 +174,9 @@ if (APPLE)
|
|||
find_library(CoreFoundation CoreFoundation)
|
||||
find_library(GLUT GLUT)
|
||||
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(TARGETS ${TARGET_NAME}
|
||||
|
|
|
@ -894,7 +894,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Space:
|
||||
case Qt::Key_Apostrophe:
|
||||
resetSensors();
|
||||
break;
|
||||
|
||||
|
@ -1051,20 +1051,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_R:
|
||||
if (isShifted) {
|
||||
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;
|
||||
case Qt::Key_Percent:
|
||||
|
@ -3749,6 +3735,10 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
|||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
scriptEngine->registerGlobalObject("SpeechRecognizer", Menu::getInstance()->getSpeechRecognizer());
|
||||
#endif
|
||||
|
||||
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
|
||||
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
||||
|
@ -3831,6 +3821,10 @@ void Application::stopAllScripts(bool restart) {
|
|||
it.value()->stop();
|
||||
qDebug() << "stopping script..." << it.key();
|
||||
}
|
||||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||
// TODO: expose animation priorities and provide a layered animation control system.
|
||||
_myAvatar->clearJointAnimationPriorities();
|
||||
}
|
||||
|
||||
void Application::stopScript(const QString &scriptName) {
|
||||
|
@ -3838,6 +3832,10 @@ void Application::stopScript(const QString &scriptName) {
|
|||
if (_scriptEnginesHash.contains(scriptURLString)) {
|
||||
_scriptEnginesHash.value(scriptURLString)->stop();
|
||||
qDebug() << "stopping script..." << scriptName;
|
||||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||
// TODO: expose animation priorities and provide a layered animation control system.
|
||||
_myAvatar->clearJointAnimationPriorities();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@ Menu::Menu() :
|
|||
_octreeStatsDialog(NULL),
|
||||
_lodToolsDialog(NULL),
|
||||
_userLocationsDialog(NULL),
|
||||
#ifdef Q_OS_MAC
|
||||
_speechRecognizer(),
|
||||
#endif
|
||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
||||
|
@ -221,19 +224,16 @@ Menu::Menu() :
|
|||
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments()));
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, this, SLOT(editAnimations()));
|
||||
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
|
||||
avatar, SLOT(updateMotionBehaviorsFromMenu()));
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::StandOnNearbyFloors, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorsFromMenu()));
|
||||
|
||||
addAvatarCollisionSubMenu(editMenu);
|
||||
|
||||
QMenu* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
|
||||
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
|
||||
_chatAction = addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Chat,
|
||||
|
@ -257,6 +257,45 @@ Menu::Menu() :
|
|||
this,
|
||||
SLOT(toggleConsole()));
|
||||
|
||||
QMenu* avatarMenu = addMenu("Avatar");
|
||||
|
||||
QMenu* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
Qt::Key_Plus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
Qt::Key_Minus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
Qt::Key_Equal,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(resetSize()));
|
||||
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ChatCircling, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
|
||||
avatar, SLOT(updateMotionBehaviorsFromMenu()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorsFromMenu()));
|
||||
|
||||
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With...");
|
||||
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll);
|
||||
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithAvatars,
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithVoxels,
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithParticles,
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithEnvironment,
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
|
||||
QMenu* viewMenu = addMenu("View");
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
|
@ -304,25 +343,6 @@ Menu::Menu() :
|
|||
Qt::CTRL | Qt::SHIFT | Qt::Key_3, false,
|
||||
&nodeBounds, SLOT(setShowParticleNodes(bool)));
|
||||
|
||||
|
||||
QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size");
|
||||
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
Qt::Key_Plus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
Qt::Key_Minus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
Qt::Key_Equal,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(resetSize()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MoveWithLean, 0, false);
|
||||
|
@ -338,111 +358,97 @@ Menu::Menu() :
|
|||
|
||||
QMenu* developerMenu = addMenu("Developer");
|
||||
|
||||
QMenu* renderOptionsMenu = developerMenu->addMenu("Rendering Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||
QMenu* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
||||
|
||||
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::AvatarsReceiveShadows, 0, true));
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu,
|
||||
MenuOption::Voxels,
|
||||
Qt::SHIFT | Qt::Key_V,
|
||||
true,
|
||||
appInstance,
|
||||
SLOT(setRenderVoxels(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu,
|
||||
MenuOption::GlowMode,
|
||||
0,
|
||||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::StringHair, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu,
|
||||
MenuOption::Voxels,
|
||||
Qt::SHIFT | Qt::Key_V,
|
||||
true,
|
||||
appInstance,
|
||||
SLOT(setRenderVoxels(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableAutoAdjustLOD);
|
||||
|
||||
QMenu* modelOptionsMenu = developerMenu->addMenu("Model Options");
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::Models, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelBounds, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementProxy, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
|
||||
|
||||
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarsReceiveShadows, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollideAsRagdoll);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
|
||||
QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
#ifdef HAVE_FACESHIFT
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
|
||||
MenuOption::Faceshift,
|
||||
0,
|
||||
true,
|
||||
appInstance->getFaceshift(),
|
||||
SLOT(setTCPEnabled(bool)));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FACEPLUS
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Faceplus, 0, true,
|
||||
appInstance->getFaceplus(), SLOT(updateEnabled()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Faceplus, 0, true,
|
||||
appInstance->getFaceplus(), SLOT(updateEnabled()));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VISAGE
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, false,
|
||||
appInstance->getVisage(), SLOT(updateEnabled()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Visage, 0, false,
|
||||
appInstance->getVisage(), SLOT(updateEnabled()));
|
||||
#endif
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::GlowWhenSpeaking, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::FocusIndicators, 0, false);
|
||||
|
||||
QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options");
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, true);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
|
||||
QMenu* modelDebugMenu = developerMenu->addMenu("Models");
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelBounds, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementProxy, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxels");
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableAutoAdjustLOD);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||
|
||||
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||
MenuOption::FilterSixense,
|
||||
0,
|
||||
true,
|
||||
appInstance->getSixenseManager(),
|
||||
SLOT(setFilter(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||
MenuOption::LowVelocityFilter,
|
||||
0,
|
||||
true,
|
||||
appInstance,
|
||||
SLOT(setLowVelocityFilter(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
QMenu* networkMenu = developerMenu->addMenu("Network");
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||
MenuOption::DisableActivityLogger,
|
||||
0,
|
||||
false,
|
||||
|
@ -451,9 +457,7 @@ Menu::Menu() :
|
|||
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::WalletPrivateKey, 0, this, SLOT(changePrivateKey()));
|
||||
|
||||
addDisabledActionAndSeparator(developerMenu, "Testing");
|
||||
|
||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
|
@ -465,8 +469,10 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
||||
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
|
||||
QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools");
|
||||
QMenu* frustumMenu = developerMenu->addMenu("View Frustum");
|
||||
addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F);
|
||||
addActionToQMenuAndActionHash(frustumMenu,
|
||||
MenuOption::FrustumRenderMode,
|
||||
|
@ -476,11 +482,7 @@ Menu::Menu() :
|
|||
updateFrustumRenderModeAction();
|
||||
|
||||
|
||||
QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools");
|
||||
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings);
|
||||
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings);
|
||||
|
||||
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
|
||||
QMenu* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
|
||||
0,
|
||||
true,
|
||||
|
@ -493,7 +495,7 @@ Menu::Menu() :
|
|||
appInstance->getAudio(),
|
||||
SLOT(toggleAudioFilter()));
|
||||
|
||||
QMenu* audioFilterMenu = audioDebugMenu->addMenu("Audio Filter Options");
|
||||
QMenu* audioFilterMenu = audioDebugMenu->addMenu("Audio Filter");
|
||||
addDisabledActionAndSeparator(audioFilterMenu, "Filter Response");
|
||||
{
|
||||
QAction *flat = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterFlat,
|
||||
|
@ -557,7 +559,7 @@ Menu::Menu() :
|
|||
appInstance->getAudio(),
|
||||
SLOT(toggleScopePause()));
|
||||
|
||||
QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope Options");
|
||||
QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
addDisabledActionAndSeparator(audioScopeMenu, "Display Frames");
|
||||
{
|
||||
QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||
|
@ -648,13 +650,17 @@ Menu::Menu() :
|
|||
appInstance->getAudio(),
|
||||
SLOT(toggleStatsShowInjectedStreams()));
|
||||
|
||||
connect(appInstance->getAudio(), SIGNAL(muteToggled()), this, SLOT(audioMuteToggled()));
|
||||
|
||||
QMenu* experimentalOptionsMenu = developerMenu->addMenu("Experimental");
|
||||
addCheckableActionToQMenuAndActionHash(experimentalOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(experimentalOptionsMenu, MenuOption::StringHair, 0, false);
|
||||
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_V,
|
||||
this,
|
||||
SLOT(pasteToVoxel()));
|
||||
|
||||
connect(appInstance->getAudio(), SIGNAL(muteToggled()), this, SLOT(audioMuteToggled()));
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QMenu* helpMenu = addMenu("Help");
|
||||
QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp);
|
||||
|
@ -692,6 +698,10 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).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");
|
||||
// in case settings is corrupt or missing loadSetting() will check for NaN
|
||||
_viewFrustumOffset.yaw = loadSetting(settings, "viewFrustumOffsetYaw", 0.0f);
|
||||
|
@ -739,6 +749,9 @@ void Menu::saveSettings(QSettings* settings) {
|
|||
settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust);
|
||||
settings->setValue("snapshotsLocation", _snapshotsLocation);
|
||||
settings->setValue("scriptsLocation", _scriptsLocation);
|
||||
#ifdef Q_OS_MAC
|
||||
settings->setValue("speechRecognitionEnabled", _speechRecognizer.getEnabled());
|
||||
#endif
|
||||
settings->beginGroup("View Frustum Offset Camera");
|
||||
settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw);
|
||||
settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch);
|
||||
|
@ -1457,7 +1470,9 @@ void Menu::toggleConsole() {
|
|||
|
||||
void Menu::audioMuteToggled() {
|
||||
QAction *muteAction = _actionHash.value(MenuOption::MuteAudio);
|
||||
muteAction->setChecked(Application::getInstance()->getAudio()->getMuted());
|
||||
if (muteAction) {
|
||||
muteAction->setChecked(Application::getInstance()->getAudio()->getMuted());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::bandwidthDetailsClosed() {
|
||||
|
@ -1629,45 +1644,31 @@ void Menu::runTests() {
|
|||
|
||||
void Menu::updateFrustumRenderModeAction() {
|
||||
QAction* frustumRenderModeAction = _actionHash.value(MenuOption::FrustumRenderMode);
|
||||
switch (_frustumDrawMode) {
|
||||
default:
|
||||
case FRUSTUM_DRAW_MODE_ALL:
|
||||
frustumRenderModeAction->setText("Render Mode - All");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_VECTORS:
|
||||
frustumRenderModeAction->setText("Render Mode - Vectors");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_PLANES:
|
||||
frustumRenderModeAction->setText("Render Mode - Planes");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_NEAR_PLANE:
|
||||
frustumRenderModeAction->setText("Render Mode - Near");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_FAR_PLANE:
|
||||
frustumRenderModeAction->setText("Render Mode - Far");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_KEYHOLE:
|
||||
frustumRenderModeAction->setText("Render Mode - Keyhole");
|
||||
break;
|
||||
if (frustumRenderModeAction) {
|
||||
switch (_frustumDrawMode) {
|
||||
default:
|
||||
case FRUSTUM_DRAW_MODE_ALL:
|
||||
frustumRenderModeAction->setText("Render Mode - All");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_VECTORS:
|
||||
frustumRenderModeAction->setText("Render Mode - Vectors");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_PLANES:
|
||||
frustumRenderModeAction->setText("Render Mode - Planes");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_NEAR_PLANE:
|
||||
frustumRenderModeAction->setText("Render Mode - Near");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_FAR_PLANE:
|
||||
frustumRenderModeAction->setText("Render Mode - Far");
|
||||
break;
|
||||
case FRUSTUM_DRAW_MODE_KEYHOLE:
|
||||
frustumRenderModeAction->setText("Render Mode - Keyhole");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) {
|
||||
// add avatar collisions subMenu to overMenu
|
||||
QMenu* subMenu = overMenu->addMenu("Collision Options");
|
||||
|
||||
Application* appInstance = Application::getInstance();
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment,
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars,
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels,
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles,
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
}
|
||||
|
||||
QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
|
||||
QList<QAction*> menuActions;
|
||||
if (menu) {
|
||||
|
@ -1887,10 +1888,9 @@ void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
|||
};
|
||||
|
||||
bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
QMenu* menuObj = getMenu(menu);
|
||||
QAction* menuItemAction = _actionHash.value(menuitem);
|
||||
if (menuObj && menuItemAction) {
|
||||
return true;
|
||||
if (menuItemAction) {
|
||||
return (getMenu(menu) != NULL);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
#include <MenuItemProperties.h>
|
||||
#include <OctreeConstants.h>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
|
||||
#include "location/LocationManager.h"
|
||||
#include "ui/PreferencesDialog.h"
|
||||
#include "ui/ChatWindow.h"
|
||||
|
@ -137,6 +141,10 @@ public:
|
|||
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
SpeechRecognizer* getSpeechRecognizer() { return &_speechRecognizer; }
|
||||
#endif
|
||||
|
||||
// User Tweakable PPS from Voxel Server
|
||||
int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; }
|
||||
void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; }
|
||||
|
@ -246,8 +254,6 @@ private:
|
|||
|
||||
void updateFrustumRenderModeAction();
|
||||
|
||||
void addAvatarCollisionSubMenu(QMenu* overMenu);
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, QMenu* menu);
|
||||
QMenu* getSubMenuFromName(const QString& menuName, QMenu* menu);
|
||||
QMenu* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
|
@ -274,6 +280,9 @@ private:
|
|||
OctreeStatsDialog* _octreeStatsDialog;
|
||||
LodToolsDialog* _lodToolsDialog;
|
||||
UserLocationsDialog* _userLocationsDialog;
|
||||
#ifdef Q_OS_MAC
|
||||
SpeechRecognizer _speechRecognizer;
|
||||
#endif
|
||||
int _maxVoxels;
|
||||
float _voxelSizeScale;
|
||||
float _oculusUIAngularSize;
|
||||
|
@ -342,25 +351,27 @@ namespace MenuOption {
|
|||
const QString AvatarsReceiveShadows = "Avatars Receive Shadows";
|
||||
const QString Bandwidth = "Bandwidth Display";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
||||
const QString BuckyBalls = "Bucky Balls";
|
||||
const QString CascadedShadows = "Cascaded";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ChatCircling = "Chat Circling";
|
||||
const QString CollideAsRagdoll = "Collide As Ragdoll";
|
||||
const QString CollideWithAvatars = "Collide With Avatars";
|
||||
const QString CollideAsRagdoll = "Collide With Self (Ragdoll)";
|
||||
const QString CollideWithAvatars = "Collide With Other Avatars";
|
||||
const QString CollideWithEnvironment = "Collide With World Boundaries";
|
||||
const QString CollideWithParticles = "Collide With Particles";
|
||||
const QString CollideWithVoxels = "Collide With Voxels";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString Console = "Console...";
|
||||
const QString ControlWithSpeech = "Control With Speech";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||
const QString DisableNackPackets = "Disable NACK Packets";
|
||||
const QString DisplayFrustum = "Display Frustum";
|
||||
const QString DisplayHands = "Display Hands";
|
||||
const QString DisplayHandTargets = "Display Hand Targets";
|
||||
const QString DisplayHands = "Show Hand Info";
|
||||
const QString DisplayHandTargets = "Show Hand Targets";
|
||||
const QString DisplayModelBounds = "Display Model Bounds";
|
||||
const QString DisplayModelElementChildProxies = "Display Model Element Children";
|
||||
const QString DisplayModelElementProxy = "Display Model Element Bounds";
|
||||
|
@ -380,7 +391,6 @@ namespace MenuOption {
|
|||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString FocusIndicators = "Focus Indicators";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FrustumRenderMode = "Render Mode";
|
||||
const QString Fullscreen = "Fullscreen";
|
||||
|
@ -391,7 +401,6 @@ namespace MenuOption {
|
|||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoTo = "Go To...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString HandsCollideWithSelf = "Collide With Self";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IncreaseVoxelSize = "Increase Voxel Size";
|
||||
|
@ -401,7 +410,6 @@ namespace MenuOption {
|
|||
const QString Login = "Login";
|
||||
const QString Log = "Log";
|
||||
const QString Logout = "Logout";
|
||||
const QString LookAtVectors = "Look-at Vectors";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString MetavoxelEditor = "Metavoxel Editor...";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
|
@ -421,13 +429,15 @@ namespace MenuOption {
|
|||
const QString Pair = "Pair";
|
||||
const QString Particles = "Particles";
|
||||
const QString PasteToVoxel = "Paste to Voxel...";
|
||||
const QString PipelineWarnings = "Show Render Pipeline Warnings";
|
||||
const QString PipelineWarnings = "Log Render Pipeline Warnings";
|
||||
const QString Preferences = "Preferences...";
|
||||
const QString Quit = "Quit";
|
||||
const QString ReloadAllScripts = "Reload All Scripts";
|
||||
const QString RenderBoundingCollisionShapes = "Bounding Collision Shapes";
|
||||
const QString RenderHeadCollisionShapes = "Head Collision Shapes";
|
||||
const QString RenderSkeletonCollisionShapes = "Skeleton Collision Shapes";
|
||||
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
|
||||
const QString RenderFocusIndicator = "Show Eye Focus";
|
||||
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
|
||||
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
||||
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString RunningScripts = "Running Scripts";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
|
@ -459,7 +469,7 @@ namespace MenuOption {
|
|||
const QString VoxelMode = "Cycle Voxel Mode";
|
||||
const QString Voxels = "Voxels";
|
||||
const QString VoxelTextures = "Voxel Textures";
|
||||
const QString WalletPrivateKey = "Wallet Private Key";
|
||||
const QString WalletPrivateKey = "Wallet Private Key...";
|
||||
}
|
||||
|
||||
void sendFakeEnterEvent();
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QMetaObject>
|
||||
#include <QObject>
|
||||
|
||||
#include "Recorder.h"
|
||||
|
||||
|
@ -241,9 +243,14 @@ void Player::stopPlaying() {
|
|||
|
||||
// Cleanup audio thread
|
||||
_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();
|
||||
_audioThread->exit();
|
||||
_audioThread->deleteLater();
|
||||
_audioThread = NULL;
|
||||
qDebug() << "Recorder::stopPlaying()";
|
||||
}
|
||||
|
||||
|
@ -309,13 +316,255 @@ bool Player::computeCurrentFrame() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void writeRecordingToFile(RecordingPointer recording, QString file) {
|
||||
// TODO
|
||||
qDebug() << "Writing recording to " << file;
|
||||
void writeRecordingToFile(RecordingPointer recording, QString filename) {
|
||||
if (!recording || recording->getFrameNumber() < 1) {
|
||||
qDebug() << "Can't save empty recording";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Writing recording to " << filename << ".";
|
||||
QElapsedTimer timer;
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)){
|
||||
return;
|
||||
}
|
||||
timer.start();
|
||||
|
||||
|
||||
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() << " ms.";
|
||||
}
|
||||
|
||||
RecordingPointer readRecordingFromFile(RecordingPointer recording, QString file) {
|
||||
// TODO
|
||||
qDebug() << "Reading recording from " << file;
|
||||
RecordingPointer readRecordingFromFile(RecordingPointer recording, QString filename) {
|
||||
qDebug() << "Reading recording from " << filename << ".";
|
||||
if (!recording) {
|
||||
recording.reset(new Recording());
|
||||
}
|
||||
|
||||
QElapsedTimer timer;
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)){
|
||||
return recording;
|
||||
}
|
||||
timer.start();
|
||||
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 in " << timer.elapsed() << " ms.";
|
||||
return recording;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -137,6 +137,8 @@ public:
|
|||
bool isPlaying() const;
|
||||
qint64 elapsed() const;
|
||||
|
||||
RecordingPointer getRecording() const { return _recording; }
|
||||
|
||||
// Those should only be called if isPlaying() returns true
|
||||
glm::quat getHeadRotation();
|
||||
float getLeanSideways();
|
||||
|
|
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
|
|
@ -302,7 +302,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
return;
|
||||
}
|
||||
|
||||
glm::vec3 toTarget = cameraPosition - Application::getInstance()->getAvatar()->getPosition();
|
||||
glm::vec3 toTarget = cameraPosition - getPosition();
|
||||
float distanceToTarget = glm::length(toTarget);
|
||||
|
||||
{
|
||||
|
@ -349,7 +349,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
}
|
||||
|
||||
// If this is the avatar being looked at, render a little ball above their head
|
||||
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::FocusIndicators)) {
|
||||
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
|
||||
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
|
||||
const float LOOK_AT_INDICATOR_OFFSET = 0.22f;
|
||||
const float LOOK_AT_INDICATOR_COLOR[] = { 0.8f, 0.0f, 0.0f, 0.75f };
|
||||
|
@ -368,10 +368,12 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
// quick check before falling into the code below:
|
||||
// (a 10 degree breadth of an almost 2 meter avatar kicks in at about 12m)
|
||||
const float MIN_VOICE_SPHERE_DISTANCE = 12.0f;
|
||||
if (distanceToTarget > MIN_VOICE_SPHERE_DISTANCE) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::BlueSpeechSphere)
|
||||
&& distanceToTarget > MIN_VOICE_SPHERE_DISTANCE) {
|
||||
|
||||
// render voice intensity sphere for avatars that are farther away
|
||||
const float MAX_SPHERE_ANGLE = 10.0f * RADIANS_PER_DEGREE;
|
||||
const float MIN_SPHERE_ANGLE = 1.0f * RADIANS_PER_DEGREE;
|
||||
const float MIN_SPHERE_ANGLE = 0.5f * RADIANS_PER_DEGREE;
|
||||
const float MIN_SPHERE_SIZE = 0.01f;
|
||||
const float SPHERE_LOUDNESS_SCALING = 0.0005f;
|
||||
const float SPHERE_COLOR[] = { 0.5f, 0.8f, 0.8f };
|
||||
|
@ -392,7 +394,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
}
|
||||
}
|
||||
|
||||
const float DISPLAYNAME_DISTANCE = 10.0f;
|
||||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
setShowDisplayName(renderMode == NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
if (renderMode != NORMAL_RENDER_MODE || (isMyAvatar() &&
|
||||
Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) {
|
||||
|
|
|
@ -91,7 +91,7 @@ public:
|
|||
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||
glm::vec3 getChestPosition() const;
|
||||
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); }
|
||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||
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(const QString& name) const;
|
||||
|
||||
glm::vec3 getAcceleration() const { return _acceleration; }
|
||||
glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||
glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
|
||||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||
|
||||
|
||||
/// Scales a world space position vector relative to the avatar position and scale
|
||||
|
|
|
@ -82,7 +82,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
void AvatarManager::renderAvatars(Avatar::RenderMode renderMode, bool selfAvatarOnly) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::renderAvatars()");
|
||||
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors);
|
||||
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors);
|
||||
|
||||
glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition();
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
|
|||
state.setRotationInConstrainedFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation);
|
||||
* joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
@ -69,7 +69,7 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ
|
|||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||
joint.rotation);
|
||||
joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
void FaceModel::updateJointState(int index) {
|
||||
|
|
|
@ -508,40 +508,162 @@ bool MyAvatar::setJointReferential(int id, int jointIndex) {
|
|||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::isRecording() const {
|
||||
bool MyAvatar::isRecording() {
|
||||
if (!_recorder) {
|
||||
return false;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(this, "isRecording", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
return _recorder && _recorder->isRecording();
|
||||
}
|
||||
|
||||
RecorderPointer MyAvatar::startRecording() {
|
||||
qint64 MyAvatar::recorderElapsed() {
|
||||
if (!_recorder) {
|
||||
return 0;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qint64 result;
|
||||
QMetaObject::invokeMethod(this, "recorderElapsed", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(qint64, result));
|
||||
return result;
|
||||
}
|
||||
return _recorder->elapsed();
|
||||
}
|
||||
|
||||
void MyAvatar::startRecording() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (!_recorder) {
|
||||
_recorder = RecorderPointer(new Recorder(this));
|
||||
}
|
||||
Application::getInstance()->getAudio()->setRecorder(_recorder);
|
||||
_recorder->startRecording();
|
||||
return _recorder;
|
||||
|
||||
}
|
||||
|
||||
void MyAvatar::stopRecording() {
|
||||
if (!_recorder) {
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "stopRecording", Qt::BlockingQueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (_recorder) {
|
||||
_recorder->stopRecording();
|
||||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::isPlaying() const {
|
||||
void MyAvatar::saveRecording(QString filename) {
|
||||
if (!_recorder) {
|
||||
qDebug() << "There is no recording to save";
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(QString, filename));
|
||||
return;
|
||||
}
|
||||
if (_recorder) {
|
||||
_recorder->saveToFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::isPlaying() {
|
||||
if (!_player) {
|
||||
return false;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(this, "isPlaying", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
return _player && _player->isPlaying();
|
||||
}
|
||||
|
||||
PlayerPointer MyAvatar::startPlaying() {
|
||||
qint64 MyAvatar::playerElapsed() {
|
||||
if (!_player) {
|
||||
return 0;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qint64 result;
|
||||
QMetaObject::invokeMethod(this, "playerElapsed", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(qint64, result));
|
||||
return result;
|
||||
}
|
||||
return _player->elapsed();
|
||||
}
|
||||
|
||||
qint64 MyAvatar::playerLength() {
|
||||
if (!_player) {
|
||||
return 0;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qint64 result;
|
||||
QMetaObject::invokeMethod(this, "playerLength", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(qint64, result));
|
||||
return result;
|
||||
}
|
||||
return _player->getRecording()->getLength();
|
||||
}
|
||||
|
||||
void MyAvatar::loadRecording(QString filename) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(QString, filename));
|
||||
return;
|
||||
}
|
||||
if (!_player) {
|
||||
_player = PlayerPointer(new Player(this));
|
||||
}
|
||||
if (_recorder) {
|
||||
_player->loadRecording(_recorder->getRecording());
|
||||
_player->startPlaying();
|
||||
|
||||
_player->loadFromFile(filename);
|
||||
}
|
||||
|
||||
void MyAvatar::loadLastRecording() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection);
|
||||
return;
|
||||
}
|
||||
return _player;
|
||||
if (!_recorder) {
|
||||
qDebug() << "There is no recording to load";
|
||||
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() {
|
||||
if (!_player) {
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (_player) {
|
||||
_player->stopPlaying();
|
||||
}
|
||||
|
@ -927,36 +1049,39 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
|
|||
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
|
||||
}
|
||||
|
||||
const float JOINT_PRIORITY = 2.0f;
|
||||
const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f;
|
||||
const float RECORDER_PRIORITY = SCRIPT_PRIORITY + 1.0f;
|
||||
|
||||
void MyAvatar::setJointRotations(QVector<glm::quat> jointRotations) {
|
||||
for (int i = 0; i < jointRotations.size(); ++i) {
|
||||
if (i < _jointData.size()) {
|
||||
_skeletonModel.setJointState(i, true, jointRotations[i], JOINT_PRIORITY + 1.0f);
|
||||
}
|
||||
int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size());
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
// HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here
|
||||
_skeletonModel.setJointState(i, true, jointRotations[i], RECORDER_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setJointData(int index, const glm::quat& rotation) {
|
||||
Avatar::setJointData(index, rotation);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, true, rotation, JOINT_PRIORITY);
|
||||
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
|
||||
_skeletonModel.setJointState(index, true, rotation, SCRIPT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointData(int index) {
|
||||
Avatar::clearJointData(index);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), JOINT_PRIORITY);
|
||||
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointsData() {
|
||||
for (int i = 0; i < _jointData.size(); ++i) {
|
||||
Avatar::clearJointData(i);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.clearJointState(i);
|
||||
}
|
||||
clearJointAnimationPriorities();
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointAnimationPriorities() {
|
||||
int numStates = _skeletonModel.getJointStateCount();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
_skeletonModel.clearJointAnimationPriority(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,15 @@ public:
|
|||
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
||||
|
||||
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; }
|
||||
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||
void updateLookAtTargetAvatar();
|
||||
|
@ -120,6 +128,8 @@ public:
|
|||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
|
||||
void clearJointAnimationPriorities();
|
||||
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
@ -134,6 +144,10 @@ public:
|
|||
/// Renders a laser pointer for UI picking
|
||||
void renderLaserPointers();
|
||||
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
|
||||
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
const PlayerPointer getPlayer() const { return _player; }
|
||||
|
||||
public slots:
|
||||
void goHome();
|
||||
void increaseSize();
|
||||
|
@ -157,14 +171,18 @@ public slots:
|
|||
bool setModelReferential(int id);
|
||||
bool setJointReferential(int id, int jointIndex);
|
||||
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
bool isRecording() const;
|
||||
RecorderPointer startRecording();
|
||||
bool isRecording();
|
||||
qint64 recorderElapsed();
|
||||
void startRecording();
|
||||
void stopRecording();
|
||||
void saveRecording(QString filename);
|
||||
|
||||
const PlayerPointer getPlayer() const { return _player; }
|
||||
bool isPlaying() const;
|
||||
PlayerPointer startPlaying();
|
||||
bool isPlaying();
|
||||
qint64 playerElapsed();
|
||||
qint64 playerLength();
|
||||
void loadRecording(QString filename);
|
||||
void loadLastRecording();
|
||||
void startPlaying();
|
||||
void stopPlaying();
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
|
|||
}
|
||||
}
|
||||
|
||||
const float PALM_PRIORITY = 3.0f;
|
||||
const float PALM_PRIORITY = DEFAULT_PRIORITY;
|
||||
const float LEAN_PRIORITY = DEFAULT_PRIORITY;
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
setTranslation(_owningAvatar->getPosition());
|
||||
|
@ -230,7 +231,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
JointState& parentState = _jointStates[parentJointIndex];
|
||||
parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat());
|
||||
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY);
|
||||
} else {
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
|
@ -243,7 +244,7 @@ void SkeletonModel::updateJointState(int index) {
|
|||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (index == geometry.leanJointIndex) {
|
||||
maybeUpdateLeanRotation(parentState, joint, state);
|
||||
maybeUpdateLeanRotation(parentState, state);
|
||||
|
||||
} else if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint, state);
|
||||
|
@ -260,17 +261,18 @@ void SkeletonModel::updateJointState(int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) {
|
||||
if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) {
|
||||
return;
|
||||
}
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
||||
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
|
||||
glm::normalize(inverse * axes[0])) * joint.rotation);
|
||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
|
||||
state.setRotationInConstrainedFrame(
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
|
||||
* state.getFBXJoint().rotation, LEAN_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
@ -576,6 +578,7 @@ SkeletonRagdoll* SkeletonModel::buildRagdoll() {
|
|||
if (!_ragdoll) {
|
||||
_ragdoll = new SkeletonRagdoll(this);
|
||||
if (_enableShapes) {
|
||||
clearShapes();
|
||||
buildShapes();
|
||||
}
|
||||
}
|
||||
|
@ -600,6 +603,7 @@ void SkeletonModel::buildShapes() {
|
|||
if (!_ragdoll) {
|
||||
_ragdoll = new SkeletonRagdoll(this);
|
||||
}
|
||||
_ragdoll->setRootIndex(geometry.rootJointIndex);
|
||||
_ragdoll->initPoints();
|
||||
QVector<VerletPoint>& points = _ragdoll->getPoints();
|
||||
|
||||
|
@ -614,15 +618,14 @@ void SkeletonModel::buildShapes() {
|
|||
float radius = uniformScale * joint.boneRadius;
|
||||
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
|
||||
Shape::Type type = joint.shapeType;
|
||||
if (i == 0 || (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON)) {
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1 || radius < EPSILON) {
|
||||
type = Shape::UNKNOWN_SHAPE;
|
||||
} else if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) {
|
||||
// this shape is forced to be a sphere
|
||||
type = Shape::SPHERE_SHAPE;
|
||||
}
|
||||
if (radius < EPSILON) {
|
||||
type = Shape::UNKNOWN_SHAPE;
|
||||
}
|
||||
Shape* shape = NULL;
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (type == Shape::SPHERE_SHAPE) {
|
||||
shape = new VerletSphereShape(radius, &(points[i]));
|
||||
shape->setEntity(this);
|
||||
|
@ -637,18 +640,16 @@ void SkeletonModel::buildShapes() {
|
|||
points[i].setMass(mass);
|
||||
totalMass += mass;
|
||||
}
|
||||
if (parentIndex != -1) {
|
||||
if (shape && parentIndex != -1) {
|
||||
// always disable collisions between joint and its parent
|
||||
if (shape) {
|
||||
disableCollisions(i, parentIndex);
|
||||
}
|
||||
disableCollisions(i, parentIndex);
|
||||
}
|
||||
_shapes.push_back(shape);
|
||||
}
|
||||
|
||||
// set the mass of the root
|
||||
if (numStates > 0) {
|
||||
points[0].setMass(totalMass);
|
||||
points[_ragdoll->getRootIndex()].setMass(totalMass);
|
||||
}
|
||||
|
||||
// This method moves the shapes to their default positions in Model frame.
|
||||
|
|
|
@ -127,7 +127,7 @@ protected:
|
|||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
void maybeUpdateLeanRotation(const JointState& parentState, JointState& state);
|
||||
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ void SkeletonRagdoll::stepForward(float deltaTime) {
|
|||
|
||||
void SkeletonRagdoll::slamPointPositions() {
|
||||
QVector<JointState>& jointStates = _model->getJointStates();
|
||||
int numStates = jointStates.size();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
const int numPoints = _points.size();
|
||||
assert(numPoints == jointStates.size());
|
||||
for (int i = _rootIndex; i < numPoints; ++i) {
|
||||
_points[i].initPosition(jointStates.at(i).getPosition());
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +50,7 @@ void SkeletonRagdoll::initPoints() {
|
|||
|
||||
initTransform();
|
||||
// one point for each joint
|
||||
QVector<JointState>& jointStates = _model->getJointStates();
|
||||
int numStates = jointStates.size();
|
||||
int numStates = _model->getJointStates().size();
|
||||
_points.fill(VerletPoint(), numStates);
|
||||
slamPointPositions();
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ void SkeletonRagdoll::buildConstraints() {
|
|||
float minBone = FLT_MAX;
|
||||
float maxBone = -FLT_MAX;
|
||||
QMultiMap<int, int> families;
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
for (int i = _rootIndex; i < numPoints; ++i) {
|
||||
const JointState& state = jointStates.at(i);
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex != -1) {
|
||||
|
@ -105,7 +105,7 @@ void SkeletonRagdoll::buildConstraints() {
|
|||
float MAX_STRENGTH = 0.6f;
|
||||
float MIN_STRENGTH = 0.05f;
|
||||
// each joint gets a MuscleConstraint to its parent
|
||||
for (int i = 1; i < numPoints; ++i) {
|
||||
for (int i = _rootIndex + 1; i < numPoints; ++i) {
|
||||
const JointState& state = jointStates.at(i);
|
||||
int p = state.getParentIndex();
|
||||
if (p == -1) {
|
||||
|
|
|
@ -389,7 +389,6 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
if (theta > EPSILON) {
|
||||
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
||||
const float AVERAGE_CARA_FRAME_TIME = 0.04f;
|
||||
const float ANGULAR_VELOCITY_MIN = 1.2f;
|
||||
const float YAW_STANDARD_DEV_DEG = 2.5f;
|
||||
|
||||
_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;
|
||||
if (coefficient[MAX] > coefficient[MIN]) {
|
||||
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 {
|
||||
return glm::clamp(currentValue - coefficient[AVG], 0.f, 1.f);
|
||||
return glm::clamp(currentValue - coefficient[AVG], 0.0f, 1.0f);
|
||||
}
|
||||
} else {
|
||||
return 0.f;
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,13 +242,11 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
|
||||
// Set blendshapes
|
||||
float EYE_MAGNIFIER = 4.0f;
|
||||
|
||||
float rightEye = (updateAndGetCoefficient(_rightEye, packet.expressions[0])) * EYE_MAGNIFIER;
|
||||
float rightEye = glm::clamp((updateAndGetCoefficient(_rightEye, packet.expressions[0])) * EYE_MAGNIFIER, 0.0f, 1.0f);
|
||||
_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;
|
||||
|
||||
// Right eye = packet.expressions[0];
|
||||
|
||||
float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]);
|
||||
if (leftBrow < 0.5f) {
|
||||
|
@ -270,9 +268,9 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
float JAW_OPEN_MAGNIFIER = 1.4f;
|
||||
_blendshapeCoefficients[_jawOpenIndex] = rescaleCoef(packet.expressions[21]) * JAW_OPEN_MAGNIFIER;
|
||||
|
||||
|
||||
_blendshapeCoefficients[_mouthSmileLeftIndex] = rescaleCoef(packet.expressions[24]);
|
||||
_blendshapeCoefficients[_mouthSmileRightIndex] = rescaleCoef(packet.expressions[23]);
|
||||
float SMILE_MULTIPLIER = 2.0f;
|
||||
_blendshapeCoefficients[_mouthSmileLeftIndex] = glm::clamp(packet.expressions[24] * SMILE_MULTIPLIER, 0.0f, 1.0f);
|
||||
_blendshapeCoefficients[_mouthSmileRightIndex] = glm::clamp(packet.expressions[23] * SMILE_MULTIPLIER, 0.0f, 1.0f);
|
||||
|
||||
|
||||
} else {
|
||||
|
|
|
@ -26,11 +26,17 @@ using namespace fs;
|
|||
using namespace std;
|
||||
|
||||
const quint16 FACESHIFT_PORT = 33433;
|
||||
float STARTING_FACESHIFT_FRAME_TIME = 0.033f;
|
||||
|
||||
Faceshift::Faceshift() :
|
||||
_tcpEnabled(true),
|
||||
_tcpRetryCount(0),
|
||||
_lastTrackingStateReceived(0),
|
||||
_averageFrameTime(STARTING_FACESHIFT_FRAME_TIME),
|
||||
_headAngularVelocity(0),
|
||||
_headLinearVelocity(0),
|
||||
_lastHeadTranslation(0),
|
||||
_filteredHeadTranslation(0),
|
||||
_eyeGazeLeftPitch(0.0f),
|
||||
_eyeGazeLeftYaw(0.0f),
|
||||
_eyeGazeRightPitch(0.0f),
|
||||
|
@ -209,23 +215,41 @@ void Faceshift::receive(const QByteArray& buffer) {
|
|||
float theta = 2 * acos(r.w);
|
||||
if (theta > EPSILON) {
|
||||
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
||||
float AVERAGE_FACESHIFT_FRAME_TIME = 0.033f;
|
||||
_headAngularVelocity = theta / AVERAGE_FACESHIFT_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag;
|
||||
_headAngularVelocity = theta / _averageFrameTime * glm::vec3(r.x, r.y, r.z) / rMag;
|
||||
} else {
|
||||
_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;
|
||||
_headTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y,
|
||||
-data.m_headTranslation.z) * TRANSLATION_SCALE;
|
||||
glm::vec3 newHeadTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y,
|
||||
-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;
|
||||
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
|
||||
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
|
||||
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -98,8 +98,12 @@ private:
|
|||
int _tcpRetryCount;
|
||||
bool _tracking;
|
||||
quint64 _lastTrackingStateReceived;
|
||||
float _averageFrameTime;
|
||||
|
||||
glm::vec3 _headAngularVelocity;
|
||||
glm::vec3 _headLinearVelocity;
|
||||
glm::vec3 _lastHeadTranslation;
|
||||
glm::vec3 _filteredHeadTranslation;
|
||||
|
||||
// degrees
|
||||
float _eyeGazeLeftPitch;
|
||||
|
|
|
@ -145,7 +145,7 @@ glm::quat JointState::getVisibleRotationInParentFrame() const {
|
|||
void JointState::restoreRotation(float fraction, float priority) {
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority == _animationPriority || _animationPriority == 0.0f) {
|
||||
setRotationInConstrainedFrame(safeMix(_rotationInConstrainedFrame, _fbxJoint->rotation, fraction));
|
||||
setRotationInConstrainedFrameInternal(safeMix(_rotationInConstrainedFrame, _fbxJoint->rotation, fraction));
|
||||
_animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ void JointState::setRotationInBindFrame(const glm::quat& rotation, float priorit
|
|||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrame(targetRotation);
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
@ -173,10 +173,6 @@ void JointState::clearTransformTranslation() {
|
|||
_visibleTransform[3][2] = 0.0f;
|
||||
}
|
||||
|
||||
void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) {
|
||||
applyRotationDelta(rotation * glm::inverse(getRotation()), true, priority);
|
||||
}
|
||||
|
||||
void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) {
|
||||
// NOTE: delta is in model-frame
|
||||
assert(_fbxJoint != NULL);
|
||||
|
@ -193,7 +189,7 @@ void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, floa
|
|||
_rotation = delta * getRotation();
|
||||
return;
|
||||
}
|
||||
setRotationInConstrainedFrame(targetRotation);
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
}
|
||||
|
||||
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
|
||||
|
@ -212,7 +208,7 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float
|
|||
if (_constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrame(targetRotation);
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
}
|
||||
|
||||
void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor) {
|
||||
|
@ -236,7 +232,17 @@ glm::quat JointState::computeVisibleParentRotation() const {
|
|||
return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation);
|
||||
}
|
||||
|
||||
void JointState::setRotationInConstrainedFrame(const glm::quat& targetRotation) {
|
||||
void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain) {
|
||||
if (priority >= _animationPriority || _animationPriority == 0.0f) {
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRotation) {
|
||||
glm::quat parentRotation = computeParentRotation();
|
||||
_rotationInConstrainedFrame = targetRotation;
|
||||
_transformChanged = true;
|
||||
|
@ -258,6 +264,11 @@ const bool JointState::rotationIsDefault(const glm::quat& rotation, float tolera
|
|||
glm::abs(rotation.w - defaultRotation.w) < tolerance;
|
||||
}
|
||||
|
||||
glm::quat JointState::getDefaultRotationInParentFrame() const {
|
||||
// NOTE: the result is constant and could be cached in the FBXJoint
|
||||
return _fbxJoint->preRotation * _fbxJoint->rotation * _fbxJoint->postRotation;
|
||||
}
|
||||
|
||||
const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const {
|
||||
assert(_fbxJoint != NULL);
|
||||
return _fbxJoint->translation;
|
||||
|
@ -267,4 +278,4 @@ void JointState::slaveVisibleTransform() {
|
|||
_visibleTransform = _transform;
|
||||
_visibleRotation = getRotation();
|
||||
_visibleRotationInConstrainedFrame = _rotationInConstrainedFrame;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <FBXReader.h>
|
||||
|
||||
const float DEFAULT_PRIORITY = 3.0f;
|
||||
|
||||
class AngularConstraint;
|
||||
|
||||
class JointState {
|
||||
|
@ -60,9 +62,6 @@ public:
|
|||
|
||||
int getParentIndex() const { return _fbxJoint->parentIndex; }
|
||||
|
||||
/// \param rotation rotation of joint in model-frame
|
||||
void setRotation(const glm::quat& rotation, bool constrain, float priority);
|
||||
|
||||
/// \param delta is in the model-frame
|
||||
void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
||||
|
||||
|
@ -84,13 +83,14 @@ public:
|
|||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
void setRotationInConstrainedFrame(const glm::quat& targetRotation);
|
||||
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false);
|
||||
void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation);
|
||||
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
|
||||
const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; }
|
||||
|
||||
const bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const;
|
||||
|
||||
glm::quat getDefaultRotationInParentFrame() const;
|
||||
const glm::vec3& getDefaultTranslationInConstrainedFrame() const;
|
||||
|
||||
|
||||
|
@ -106,6 +106,7 @@ public:
|
|||
glm::quat computeVisibleParentRotation() const;
|
||||
|
||||
private:
|
||||
void setRotationInConstrainedFrameInternal(const glm::quat& targetRotation);
|
||||
/// debug helper function
|
||||
void loadBindRotation();
|
||||
|
||||
|
|
|
@ -438,7 +438,7 @@ void Model::reset() {
|
|||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation);
|
||||
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -695,8 +695,13 @@ bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
|
|||
void Model::clearJointState(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.setRotationInConstrainedFrame(glm::quat());
|
||||
state._animationPriority = 0.0f;
|
||||
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::clearJointAnimationPriority(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
_jointStates[index]._animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,8 +710,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa
|
|||
JointState& state = _jointStates[index];
|
||||
if (priority >= state._animationPriority) {
|
||||
if (valid) {
|
||||
state.setRotationInConstrainedFrame(rotation);
|
||||
state._animationPriority = priority;
|
||||
state.setRotationInConstrainedFrame(rotation, priority);
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
}
|
||||
|
@ -1739,10 +1743,7 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
JointState& state = _model->_jointStates[mapping];
|
||||
if (_priority >= state._animationPriority) {
|
||||
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction));
|
||||
state._animationPriority = _priority;
|
||||
}
|
||||
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,6 +121,9 @@ public:
|
|||
/// Clear the joint states
|
||||
void clearJointState(int index);
|
||||
|
||||
/// Clear the joint animation priority
|
||||
void clearJointAnimationPriority(int 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);
|
||||
|
||||
|
|
|
@ -295,7 +295,6 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS
|
|||
// filename if the directory is valid.
|
||||
QString path = "";
|
||||
QFileInfo fileInfo = QFileInfo(directory);
|
||||
qDebug() << "File: " << directory << fileInfo.isFile();
|
||||
if (fileInfo.isDir()) {
|
||||
fileInfo.setFile(directory, "__HIFI_INVALID_FILE__");
|
||||
path = fileInfo.filePath();
|
||||
|
@ -303,7 +302,6 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS
|
|||
|
||||
QFileDialog fileDialog(Application::getInstance()->getWindow(), title, path, nameFilter);
|
||||
fileDialog.setAcceptMode(acceptMode);
|
||||
qDebug() << "Opening!";
|
||||
QUrl fileUrl(directory);
|
||||
if (acceptMode == QFileDialog::AcceptSave) {
|
||||
fileDialog.setFileMode(QFileDialog::Directory);
|
||||
|
|
|
@ -282,19 +282,10 @@ void Stats::display(
|
|||
pingVoxel = totalPingVoxel/voxelServerCount;
|
||||
}
|
||||
|
||||
|
||||
Audio* audio = Application::getInstance()->getAudio();
|
||||
|
||||
lines = _expanded ? 4 : 3;
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
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];
|
||||
sprintf(audioPing, "Audio ping: %d", pingAudio);
|
||||
|
@ -698,27 +689,6 @@ void Stats::display(
|
|||
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()) {
|
||||
QVector<glm::quat> result;
|
||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
||||
"getJointRotation", Qt::BlockingQueuedConnection,
|
||||
"getJointRotations", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QVector<glm::quat>, result));
|
||||
return result;
|
||||
}
|
||||
|
@ -702,7 +702,7 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
|
|||
if (QThread::currentThread() != thread()) {
|
||||
QVector<glm::quat> result;
|
||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
||||
"setJointRotation", Qt::BlockingQueuedConnection,
|
||||
"setJointRotations", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(QVector<glm::quat>, jointRotations));
|
||||
}
|
||||
for (int i = 0; i < jointRotations.size(); ++i) {
|
||||
|
|
|
@ -78,6 +78,8 @@ KeyEvent::KeyEvent(const QKeyEvent& event) {
|
|||
text = "LEFT";
|
||||
} else if (key == Qt::Key_Right) {
|
||||
text = "RIGHT";
|
||||
} else if (key == Qt::Key_Space) {
|
||||
text = "SPACE";
|
||||
} else if (key == Qt::Key_Escape) {
|
||||
text = "ESC";
|
||||
} else if (key == Qt::Key_Tab) {
|
||||
|
@ -220,6 +222,8 @@ void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
|
|||
} else if (event.text.toUpper() == "RIGHT") {
|
||||
event.key = Qt::Key_Right;
|
||||
event.isKeypad = true;
|
||||
} else if (event.text.toUpper() == "SPACE") {
|
||||
event.key = Qt::Key_Space;
|
||||
} else if (event.text.toUpper() == "ESC") {
|
||||
event.key = Qt::Key_Escape;
|
||||
} else if (event.text.toUpper() == "TAB") {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "SharedUtil.h" // for EPSILON
|
||||
|
||||
Ragdoll::Ragdoll() : _massScale(1.0f), _translation(0.0f), _translationInSimulationFrame(0.0f),
|
||||
_accumulatedMovement(0.0f), _simulation(NULL) {
|
||||
_rootIndex(0), _accumulatedMovement(0.0f), _simulation(NULL) {
|
||||
}
|
||||
|
||||
Ragdoll::~Ragdoll() {
|
||||
|
@ -35,7 +35,7 @@ void Ragdoll::stepForward(float deltaTime) {
|
|||
updateSimulationTransforms(_translation - _simulation->getTranslation(), _rotation);
|
||||
}
|
||||
int numPoints = _points.size();
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
for (int i = _rootIndex; i < numPoints; ++i) {
|
||||
_points[i].integrateForward();
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,9 @@ void Ragdoll::initTransform() {
|
|||
}
|
||||
|
||||
void Ragdoll::setTransform(const glm::vec3& translation, const glm::quat& rotation) {
|
||||
_translation = translation;
|
||||
if (translation != _translation) {
|
||||
_translation = translation;
|
||||
}
|
||||
_rotation = rotation;
|
||||
}
|
||||
|
||||
|
@ -95,7 +97,7 @@ void Ragdoll::updateSimulationTransforms(const glm::vec3& translation, const glm
|
|||
|
||||
// apply the deltas to all ragdollPoints
|
||||
int numPoints = _points.size();
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
for (int i = _rootIndex; i < numPoints; ++i) {
|
||||
_points[i].move(deltaPosition, deltaRotation, _translationInSimulationFrame);
|
||||
}
|
||||
|
||||
|
@ -111,7 +113,7 @@ void Ragdoll::setMassScale(float scale) {
|
|||
if (scale != _massScale) {
|
||||
float rescale = scale / _massScale;
|
||||
int numPoints = _points.size();
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
for (int i = _rootIndex; i < numPoints; ++i) {
|
||||
_points[i].setMass(rescale * _points[i].getMass());
|
||||
}
|
||||
_massScale = scale;
|
||||
|
@ -122,10 +124,10 @@ void Ragdoll::removeRootOffset(bool accumulateMovement) {
|
|||
const int numPoints = _points.size();
|
||||
if (numPoints > 0) {
|
||||
// shift all points so that the root aligns with the the ragdoll's position in the simulation
|
||||
glm::vec3 offset = _translationInSimulationFrame - _points[0]._position;
|
||||
glm::vec3 offset = _translationInSimulationFrame - _points[_rootIndex]._position;
|
||||
float offsetLength = glm::length(offset);
|
||||
if (offsetLength > EPSILON) {
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
for (int i = _rootIndex; i < numPoints; ++i) {
|
||||
_points[i].shift(offset);
|
||||
}
|
||||
const float MIN_ROOT_OFFSET = 0.02f;
|
||||
|
|
|
@ -52,6 +52,10 @@ public:
|
|||
void setMassScale(float scale);
|
||||
float getMassScale() const { return _massScale; }
|
||||
|
||||
// the ragdoll's rootIndex (within a Model's joints) is not always zero so must be settable
|
||||
void setRootIndex(int index) { _rootIndex = index; }
|
||||
int getRootIndex() const { return _rootIndex; }
|
||||
|
||||
void clearConstraintsAndPoints();
|
||||
virtual void initPoints() = 0;
|
||||
virtual void buildConstraints() = 0;
|
||||
|
@ -66,6 +70,7 @@ protected:
|
|||
glm::quat _rotation; // world-frame
|
||||
glm::vec3 _translationInSimulationFrame;
|
||||
glm::quat _rotationInSimulationFrame;
|
||||
int _rootIndex;
|
||||
|
||||
QVector<VerletPoint> _points;
|
||||
QVector<DistanceConstraint*> _boneConstraints;
|
||||
|
|
Loading…
Reference in a new issue