Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2014-10-01 11:55:27 -07:00
commit 1ca212f43d
30 changed files with 568 additions and 277 deletions

View file

@ -644,7 +644,7 @@ void AudioMixer::run() {
QJsonObject audioGroupObject = settingsObject[AUDIO_GROUP_KEY].toObject();
// check the payload to see if we have asked for dynamicJitterBuffer support
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic-jitter-buffer";
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer";
_streamSettings._dynamicJitterBuffers = audioGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
if (_streamSettings._dynamicJitterBuffers) {
qDebug() << "Enable dynamic jitter buffers.";
@ -653,21 +653,21 @@ void AudioMixer::run() {
}
bool ok;
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static-desired-jitter-buffer-frames";
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames";
_streamSettings._staticDesiredJitterBufferFrames = audioGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok);
if (!ok) {
_streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES;
}
qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames;
const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max-frames-over-desired";
const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max_frames_over_desired";
_streamSettings._maxFramesOverDesired = audioGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok);
if (!ok) {
_streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED;
}
qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired;
const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use-stdev-for-desired-calc";
const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc";
_streamSettings._useStDevForJitterCalc = audioGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool();
if (_streamSettings._useStDevForJitterCalc) {
qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled";
@ -675,28 +675,28 @@ void AudioMixer::run() {
qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled";
}
const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window-starve-threshold";
const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold";
_streamSettings._windowStarveThreshold = audioGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok);
if (!ok) {
_streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD;
}
qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold;
const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window-seconds-for-desired-calc-on-too-many-starves";
const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window_seconds_for_desired_calc_on_too_many_starves";
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok);
if (!ok) {
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES;
}
qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds";
const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window-seconds-for-desired-reduction";
const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window_seconds_for_desired_reduction";
_streamSettings._windowSecondsForDesiredReduction = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok);
if (!ok) {
_streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION;
}
qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds";
const QString REPETITION_WITH_FADE_JSON_KEY = "repetition-with-fade";
const QString REPETITION_WITH_FADE_JSON_KEY = "repetition_with_fade";
_streamSettings._repetitionWithFade = audioGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool();
if (_streamSettings._repetitionWithFade) {
qDebug() << "Repetition with fade enabled";
@ -704,13 +704,13 @@ void AudioMixer::run() {
qDebug() << "Repetition with fade disabled";
}
const QString PRINT_STREAM_STATS_JSON_KEY = "print-stream-stats";
const QString PRINT_STREAM_STATS_JSON_KEY = "print_stream_stats";
_printStreamStats = audioGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool();
if (_printStreamStats) {
qDebug() << "Stream stats will be printed to stdout";
}
const QString FILTER_KEY = "enable-filter";
const QString FILTER_KEY = "enable_filter";
if (audioGroupObject[FILTER_KEY].isBool()) {
_enableFilter = audioGroupObject[FILTER_KEY].toBool();
}
@ -718,7 +718,7 @@ void AudioMixer::run() {
qDebug() << "Filter enabled";
}
const QString UNATTENUATED_ZONE_KEY = "unattenuated-zone";
const QString UNATTENUATED_ZONE_KEY = "unattenuated_zone";
QString unattenuatedZoneString = audioGroupObject[UNATTENUATED_ZONE_KEY].toString();
if (!unattenuatedZoneString.isEmpty()) {
@ -742,7 +742,7 @@ void AudioMixer::run() {
<< QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z);
}
const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation-per-doubling-in-distance";
const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation_per_doubling_in_distance";
if (audioGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].isString()) {
bool ok = false;
float attenuation = audioGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok);

View file

@ -4,9 +4,9 @@
"label": "Metaverse Registration",
"settings": [
{
"name": "access-token",
"name": "access_token",
"label": "Access Token",
"help": "This is an access token generated on the <a href='https://data.highfidelity.io/tokens'>My Tokens</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account."
"help": "This is an access token generated on the <a href='https://data.highfidelity.io/tokens' target='_blank'>My Tokens</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account."
},
{
"name": "id",
@ -20,12 +20,12 @@
"label": "Security",
"settings": [
{
"name": "http-username",
"name": "http_username",
"label": "HTTP Username",
"help": "Username used for basic HTTP authentication."
},
{
"name": "http-password",
"name": "http_password",
"label": "HTTP Password",
"type": "password",
"help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.",
@ -39,20 +39,20 @@
"assignment-types": [0],
"settings": [
{
"name": "enable-filter",
"name": "enable_filter",
"type": "checkbox",
"label": "Enable Positional Filter",
"help": "positional audio stream uses lowpass filter",
"default": true
},
{
"name": "unattenuated-zone",
"name": "unattenuated_zone",
"label": "Unattenuated Zone",
"help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)",
"placeholder": "no zone"
},
{
"name": "attenuation-per-doubling-in-distance",
"name": "attenuation_per_doubling_in_distance",
"label": "Attenuattion per doubling in distance",
"help": "Factor between 0.0 and 1.0 (0.0: No attenuation, 1.0: extreme attenuation)",
"placeholder": "0.18",
@ -60,7 +60,7 @@
"advanced": true
},
{
"name": "dynamic-jitter-buffer",
"name": "dynamic_jitter_buffer",
"type": "checkbox",
"label": "Dynamic Jitter Buffers",
"help": "dynamically buffer client audio based on perceived jitter in packet receipt timing",
@ -68,7 +68,7 @@
"advanced": true
},
{
"name": "static-desired-jitter-buffer-frames",
"name": "static_desired_jitter_buffer_frames",
"label": "Static Desired Jitter Buffer Frames",
"help": "If dynamic jitter buffers is disabled, this determines the target number of frames maintained by the AudioMixer's jitter buffers",
"placeholder": "1",
@ -76,7 +76,7 @@
"advanced": true
},
{
"name": "max-frames-over-desired",
"name": "max_frames_over_desired",
"label": "Max Frames Over Desired",
"help": "The highest number of frames an AudioMixer's ringbuffer can exceed the desired jitter buffer frames by",
"placeholder": "10",
@ -84,7 +84,7 @@
"advanced": true
},
{
"name": "use-stdev-for-desired-calc",
"name": "use_stdev_for_desired_calc",
"type": "checkbox",
"label": "Use Stdev for Desired Jitter Frames Calc:",
"help": "use Philip's method (stdev of timegaps) to calculate desired jitter frames (otherwise Fred's max timegap method is used)",
@ -92,7 +92,7 @@
"advanced": true
},
{
"name": "window-starve-threshold",
"name": "window_starve_threshold",
"label": "Window Starve Threshold",
"help": "If this many starves occur in an N-second window (N is the number in the next field), then the desired jitter frames will be re-evaluated using Window A.",
"placeholder": "3",
@ -100,7 +100,7 @@
"advanced": true
},
{
"name": "window-seconds-for-desired-calc-on-too-many-starves",
"name": "window_seconds_for_desired_calc_on_too_many_starves",
"label": "Timegaps Window (A) Seconds:",
"help": "Window A contains a history of timegaps. Its max timegap is used to re-evaluate the desired jitter frames when too many starves occur within it.",
"placeholder": "50",
@ -108,7 +108,7 @@
"advanced": true
},
{
"name": "window-seconds-for-desired-reduction",
"name": "window_seconds_for_desired_reduction",
"label": "Timegaps Window (B) Seconds:",
"help": "Window B contains a history of timegaps. Its max timegap is used as a ceiling for the desired jitter frames value.",
"placeholder": "10",
@ -116,7 +116,7 @@
"advanced": true
},
{
"name": "repetition-with-fade",
"name": "repetition_with_fade",
"type": "checkbox",
"label": "Repetition with Fade:",
"help": "dropped frames and mixing during starves repeat the last frame, eventually fading to silence",
@ -124,7 +124,7 @@
"advanced": true
},
{
"name": "print-stream-stats",
"name": "print_stream_stats",
"type": "checkbox",
"label": "Print Stream Stats:",
"help": "audio upstream and downstream stats of each agent printed to audio-mixer stdout",

View file

@ -6,7 +6,7 @@
</head>
<body>
<pre id='editor' style='font-size: 14px;'><!--#include file="placeholder.js"--></object></pre>
<script src='../js/jquery-2.0.3.min.js'></script>
<script src='../js/jquery.min.js'></script>
<script src='js/ace/ace.js' type='text/javascript'></script>
<script src='js/assignment.js' type='text/javascript'></script>
<div class='big-button' id='deploy-button'>

View file

@ -73,4 +73,4 @@ span.port {
#small-save-button {
width: 100%;
margin-bottom: 15px;
}
}

View file

@ -1,4 +1,4 @@
</div>
<script src='/js/jquery-2.1.1.min.js'></script>
<script src='/js/jquery.min.js'></script>
<script src='/js/bootstrap.min.js'></script>
<script src='/js/domain-server.js'></script>

File diff suppressed because one or more lines are too long

View file

@ -4,9 +4,7 @@ var Settings = {
var viewHelpers = {
getFormGroup: function(groupName, setting, values, isAdvanced, isLocked) {
setting_id = groupName + "_" + setting.name
console.log(setting.name + " in " + groupName + " is " + isLocked)
setting_name = groupName + "." + setting.name
form_group = "<div class='form-group" + (isAdvanced ? " advanced-setting" : "") + "'>"
@ -26,22 +24,21 @@ var viewHelpers = {
if (setting.type === 'checkbox') {
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
form_group += "<label for='" + setting_id + "'>"
form_group += "<input type='checkbox' id='" + setting_id + "' " +
form_group += "<label for='" + setting_name + "'>"
form_group += "<input type='checkbox' name='" + setting_name + "' " +
(setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
form_group += " " + setting.help + "</label>";
form_group += "</div>"
} else {
input_type = _.has(setting, 'type') ? setting.type : "text"
form_group += "<label for='" + setting_id + "' class='" + label_class + "'>" + setting.label + "</label>";
form_group += "<input type='" + input_type + "' class='form-control' id='" + setting_id +
form_group += "<label for='" + setting_name + "' class='" + label_class + "'>" + setting.label + "</label>";
form_group += "<input type='" + input_type + "' class='form-control' name='" + setting_name +
"' placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
"' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>"
form_group += "<span class='help-block'>" + setting.help + "</span>"
}
form_group += "</div>"
return form_group
}
@ -89,6 +86,10 @@ $(document).ready(function(){
$(this).blur()
})
$('#settings-form').on('click', '#choose-domain-btn', function(){
chooseFromHighFidelityDomains($(this))
})
var panelsSource = $('#panels-template').html()
Settings.panelsTemplate = _.template(panelsSource)
@ -108,16 +109,24 @@ function reloadSettings() {
$('.nav-stacked').html(Settings.sidebarTemplate(data))
$('#panels').html(Settings.panelsTemplate(data))
Settings.initialValues = form2js('settings-form', "_", false, cleanupFormValues, true);
Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true);
// add tooltip to locked settings
$('label.locked').tooltip({
placement: 'right',
title: 'This setting is in the master config file and cannot be changed'
})
appendDomainSelectionModal()
});
}
function appendDomainSelectionModal() {
var metaverseInput = $("[name='metaverse.id']");
var chooseButton = $("<button type='button' id='choose-domain-btn' class='btn btn-primary' style='margin-top:10px'>Choose ID from my domains</button>");
metaverseInput.after(chooseButton);
}
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
$('body').on('click', '.save-button', function(e){
@ -127,7 +136,9 @@ $('body').on('click', '.save-button', function(e){
});
// grab a JSON representation of the form via form2js
var formJSON = form2js('settings-form', "_", false, cleanupFormValues, true);
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
console.log(formJSON);
// re-enable all inputs
$("input").each(function(){
@ -162,13 +173,13 @@ function badgeSidebarForDifferences(changedInput) {
var panelParentID = changedInput.closest('.panel').attr('id')
// get a JSON representation of that section
var rootJSON = form2js(panelParentID, "_", false, cleanupFormValues, true);
var rootJSON = form2js(panelParentID, ".", false, cleanupFormValues, true);
var panelJSON = rootJSON[panelParentID]
var badgeValue = 0
for (var setting in panelJSON) {
if (panelJSON[setting] != Settings.initialValues[panelParentID][ setting]) {
if (panelJSON[setting] != Settings.initialValues[panelParentID][setting]) {
badgeValue += 1
}
}
@ -208,7 +219,7 @@ function showRestartModal() {
function cleanupFormValues(node) {
if (node.type && node.type === 'checkbox') {
return { name: node.id, value: node.checked ? true : false };
return { name: node.name, value: node.checked ? true : false };
} else {
return false;
}
@ -220,4 +231,71 @@ function showAlertMessage(message, isSuccess) {
alertBox.addClass(isSuccess ? 'alert-success' : 'alert-danger');
alertBox.html(message);
alertBox.fadeIn();
}
function chooseFromHighFidelityDomains(clickedButton) {
// setup the modal to help user pick their domain
if (Settings.initialValues.metaverse.access_token) {
// add a spinner to the choose button
clickedButton.html("Loading domains...")
clickedButton.attr('disabled', 'disabled')
// get a list of user domains from data-web
data_web_domains_url = "https://data.highfidelity.io/api/v1/domains?access_token="
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
modal_buttons = {
cancel: {
label: 'Cancel',
className: 'btn-default'
}
}
if (data.data.domains.length) {
// setup a select box for the returned domains
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>"
domain_select = $("<select id='domain-name-select' class='form-control'></select>")
_.each(data.data.domains, function(domain){
domain_select.append("<option value='" + domain.id + "'>" + domain.name + "</option>")
})
modal_body += "<label for='domain-name-select'>Domains</label>" + domain_select[0].outerHTML
modal_buttons["success"] = {
label: 'Choose domain',
callback: function() {
domainID = $('#domain-name-select').val()
// set the domain ID on the form
$("[name='metaverse.id']").val(domainID).change();
}
}
} else {
modal_buttons["success"] = {
label: 'Create new domain',
callback: function() {
window.open("https://data.highfidelity.io/domains", '_blank');
}
}
modal_body = "<p>You do not have any domains in your High Fidelity account." +
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
}
bootbox.dialog({
title: "Choose matching domain",
message: modal_body,
buttons: modal_buttons
})
// remove the spinner from the choose button
clickedButton.html("Choose from my domains")
clickedButton.removeAttr('disabled')
})
} else {
bootbox.alert({
message: "You must have an access token to query your High Fidelity domains.<br><br>" +
"Please follow the instructions on the settings page to add an access token.",
title: "Access token required"
})
}
}

View file

@ -75,6 +75,7 @@
<!--#include virtual="footer.html"-->
<script src='/js/underscore-min.js'></script>
<script src='/js/bootbox.min.js'></script>
<script src='/js/settings.js'></script>
<script src='/js/form2js.min.js'></script>
<!--#include virtual="page-end.html"-->

View file

@ -81,6 +81,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
void DomainServer::restart() {
qDebug() << "domain-server is restarting.";
// make sure all static instances are reset
LimitedNodeList::getInstance()->reset();
AccountManager::getInstance(true);
exit(DomainServer::EXIT_CODE_REBOOT);
}
@ -1396,8 +1401,8 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
const QString BASIC_AUTH_USERNAME_KEY_PATH = "security.http-username";
const QString BASIC_AUTH_PASSWORD_KEY_PATH = "security.http-password";
const QString BASIC_AUTH_USERNAME_KEY_PATH = "security.http_username";
const QString BASIC_AUTH_PASSWORD_KEY_PATH = "security.http_password";
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";

View file

@ -39,6 +39,28 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
_configMap.loadMasterAndUserConfig(argumentList);
// for now we perform a temporary transition from http-username and http-password to http_username and http_password
const QVariant* oldUsername = valueForKeyPath(_configMap.getUserConfig(), "security.http-username");
const QVariant* oldPassword = valueForKeyPath(_configMap.getUserConfig(), "security.http-password");
if (oldUsername || oldPassword) {
QVariantMap& settingsMap = *reinterpret_cast<QVariantMap*>(_configMap.getUserConfig()["security"].data());
// remove old keys, move to new format
if (oldUsername) {
settingsMap["http_username"] = oldUsername->toString();
settingsMap.remove("http-username");
}
if (oldPassword) {
settingsMap["http_password"] = oldPassword->toString();
settingsMap.remove("http-password");
}
// save the updated settings
persistToFile();
}
}
const QString SETTINGS_PATH = "/settings.json";

View file

@ -2745,7 +2745,7 @@ function setupModelMenus() {
}
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List", afterItem: "Models" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
@ -2796,6 +2796,128 @@ var dimensionY;
var dimensionZ;
var rescalePercentage;
function showPropertiesForm() {
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
var properties = propertiesForEditedEntity;
var array = new Array();
var index = 0;
var decimals = 3;
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
index++;
array.push({ label: "Animation FPS:", value: properties.animationFPS });
index++;
array.push({ label: "Animation Frame:", value: properties.animationFrameIndex });
index++;
}
array.push({ label: "Position:", type: "header" });
index++;
array.push({ label: "X:", value: properties.position.x.toFixed(decimals) });
index++;
array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) });
index++;
array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) });
index++;
array.push({ label: "Registration X:", value: properties.registrationPoint.x.toFixed(decimals) });
index++;
array.push({ label: "Registration Y:", value: properties.registrationPoint.y.toFixed(decimals) });
index++;
array.push({ label: "Registration Z:", value: properties.registrationPoint.z.toFixed(decimals) });
index++;
array.push({ label: "Rotation:", type: "header" });
index++;
var angles = Quat.safeEulerAngles(properties.rotation);
array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) });
index++;
array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) });
index++;
array.push({ label: "Roll:", value: angles.z.toFixed(decimals) });
index++;
array.push({ label: "Dimensions:", type: "header" });
index++;
array.push({ label: "Width:", value: properties.dimensions.x.toFixed(decimals) });
dimensionX = index;
index++;
array.push({ label: "Height:", value: properties.dimensions.y.toFixed(decimals) });
dimensionY = index;
index++;
array.push({ label: "Depth:", value: properties.dimensions.z.toFixed(decimals) });
dimensionZ = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Reset to Natural Dimensions", name: "resetDimensions" });
index++;
array.push({ label: "Rescale Percentage:", value: 100 });
rescalePercentage = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Rescale", name: "rescaleDimensions" });
index++;
array.push({ label: "Velocity:", type: "header" });
index++;
array.push({ label: "Linear X:", value: properties.velocity.x.toFixed(decimals) });
index++;
array.push({ label: "Linear Y:", value: properties.velocity.y.toFixed(decimals) });
index++;
array.push({ label: "Linear Z:", value: properties.velocity.z.toFixed(decimals) });
index++;
array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) });
index++;
array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) });
index++;
array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) });
index++;
array.push({ label: "Angular Roll:", value: properties.angularVelocity.z.toFixed(decimals) });
index++;
array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) });
index++;
array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) });
index++;
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
index++;
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
index++;
array.push({ label: "Collisions:", type: "header" });
index++;
array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) });
index++;
array.push({ label: "Ignore for Collisions:", value: properties.ignoreForCollisions });
index++;
array.push({ label: "Collisions Will Move:", value: properties.collisionsWillMove });
index++;
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
index++;
array.push({ label: "Visible:", value: properties.visible });
index++;
if (properties.type == "Box" || properties.type == "Sphere") {
array.push({ label: "Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.color.red });
index++;
array.push({ label: "Green:", value: properties.color.green });
index++;
array.push({ label: "Blue:", value: properties.color.blue });
index++;
}
array.push({ button: "Cancel" });
index++;
editEntityFormArray = array;
Window.nonBlockingForm("Edit Properties", array);
}
function handeMenuEvent(menuItem) {
print("menuItemEvent() in JS... menuItem=" + menuItem);
if (menuItem == "Delete") {
@ -2823,6 +2945,33 @@ function handeMenuEvent(menuItem) {
} else {
print(" Delete Entity.... not holding...");
}
} else if (menuItem == "Model List") {
var models = new Array();
models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE);
for (var i = 0; i < models.length; i++) {
models[i].properties = Entities.getEntityProperties(models[i]);
models[i].toString = function() {
var modelname = decodeURIComponent(
this.properties.modelURL.indexOf("/") != -1 ?
this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) :
this.properties.modelURL);
return "[" + this.properties.type + "] " + modelname;
};
}
var form = [{label: "Model: ", options: models}];
form.push({label: "Action: ", options: ["Properties", "Delete", "Teleport"]});
form.push({ button: "Cancel" });
if (Window.form("Model List", form)) {
var selectedModel = form[0].value;
if (form[1].value == "Properties") {
editModelID = selectedModel;
showPropertiesForm();
} else if (form[1].value == "Delete") {
Entities.deleteEntity(selectedModel);
} else if (form[1].value == "Teleport") {
MyAvatar.position = selectedModel.properties.position;
}
}
} else if (menuItem == "Edit Properties...") {
editModelID = -1;
if (leftController.grabbing) {
@ -2839,126 +2988,7 @@ function handeMenuEvent(menuItem) {
}
if (editModelID != -1) {
print(" Edit Properties.... about to edit properties...");
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
var properties = propertiesForEditedEntity;
var array = new Array();
var index = 0;
var decimals = 3;
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
index++;
array.push({ label: "Animation FPS:", value: properties.animationFPS });
index++;
array.push({ label: "Animation Frame:", value: properties.animationFrameIndex });
index++;
}
array.push({ label: "Position:", type: "header" });
index++;
array.push({ label: "X:", value: properties.position.x.toFixed(decimals) });
index++;
array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) });
index++;
array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) });
index++;
array.push({ label: "Registration X:", value: properties.registrationPoint.x.toFixed(decimals) });
index++;
array.push({ label: "Registration Y:", value: properties.registrationPoint.y.toFixed(decimals) });
index++;
array.push({ label: "Registration Z:", value: properties.registrationPoint.z.toFixed(decimals) });
index++;
array.push({ label: "Rotation:", type: "header" });
index++;
var angles = Quat.safeEulerAngles(properties.rotation);
array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) });
index++;
array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) });
index++;
array.push({ label: "Roll:", value: angles.z.toFixed(decimals) });
index++;
array.push({ label: "Dimensions:", type: "header" });
index++;
array.push({ label: "Width:", value: properties.dimensions.x.toFixed(decimals) });
dimensionX = index;
index++;
array.push({ label: "Height:", value: properties.dimensions.y.toFixed(decimals) });
dimensionY = index;
index++;
array.push({ label: "Depth:", value: properties.dimensions.z.toFixed(decimals) });
dimensionZ = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Reset to Natural Dimensions", name: "resetDimensions" });
index++;
array.push({ label: "Rescale Percentage:", value: 100 });
rescalePercentage = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Rescale", name: "rescaleDimensions" });
index++;
array.push({ label: "Velocity:", type: "header" });
index++;
array.push({ label: "Linear X:", value: properties.velocity.x.toFixed(decimals) });
index++;
array.push({ label: "Linear Y:", value: properties.velocity.y.toFixed(decimals) });
index++;
array.push({ label: "Linear Z:", value: properties.velocity.z.toFixed(decimals) });
index++;
array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) });
index++;
array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) });
index++;
array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) });
index++;
array.push({ label: "Angular Roll:", value: properties.angularVelocity.z.toFixed(decimals) });
index++;
array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) });
index++;
array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) });
index++;
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
index++;
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
index++;
array.push({ label: "Collisions:", type: "header" });
index++;
array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) });
index++;
array.push({ label: "Ignore for Collisions:", value: properties.ignoreForCollisions });
index++;
array.push({ label: "Collisions Will Move:", value: properties.collisionsWillMove });
index++;
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
index++;
array.push({ label: "Visible:", value: properties.visible });
index++;
if (properties.type == "Box" || properties.type == "Sphere") {
array.push({ label: "Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.color.red });
index++;
array.push({ label: "Green:", value: properties.color.green });
index++;
array.push({ label: "Blue:", value: properties.color.blue });
index++;
}
array.push({ button: "Cancel" });
index++;
editEntityFormArray = array;
Window.nonBlockingForm("Edit Properties", array);
showPropertiesForm(editModelID);
}
} else if (menuItem == "Paste Models") {
modelImporter.paste();

View file

@ -13,7 +13,10 @@
var leapHands = (function () {
var hands,
var isOnHMD,
LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip
HMD_OFFSET = 0.100, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
hands,
wrists,
NUM_HANDS = 2, // 0 = left; 1 = right
fingers,
@ -188,8 +191,6 @@ var leapHands = (function () {
function setUp() {
calibrationStatus = UNCALIBRATED;
// TODO: Leap Motion controller joint naming doesn't match up with skeleton joint naming; numbers are out by 1.
hands = [
@ -265,6 +266,20 @@ var leapHands = (function () {
{ jointName: "RightHandPinky3", controller: Controller.createInputController("Spatial", "joint_R_pinky4") }
]
];
isOnHMD = Menu.isOptionChecked("Leap Motion on HMD");
if (isOnHMD) {
print("Leap Motion is on HMD");
// Offset of Leap Motion origin from physical eye position
hands[0].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET };
hands[1].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET };
calibrationStatus = CALIBRATED;
} else {
print("Leap Motion is on desk");
calibrationStatus = UNCALIBRATED;
}
}
function moveHands() {
@ -278,7 +293,9 @@ var leapHands = (function () {
handYaw,
handRotation,
wristAbsRotation,
locRotation;
locRotation,
cameraOrientation,
inverseAvatarOrientation;
for (h = 0; h < NUM_HANDS; h += 1) {
side = h === 0 ? -1.0 : 1.0;
@ -291,35 +308,82 @@ var leapHands = (function () {
}
// Hand position ...
handOffset = hands[h].controller.getAbsTranslation();
handOffset = {
x: -handOffset.x,
y: hands[h].zeroPosition.y + handOffset.y,
z: hands[h].zeroPosition.z - handOffset.z
};
if (isOnHMD) {
// TODO: 2.0* scale factor should not be necessary; Leap Motion controller code needs investigating.
handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
wristAbsRotation = wrists[h].controller.getAbsRotation();
handPitch = 2.0 * -wristAbsRotation.x;
handYaw = 2.0 * wristAbsRotation.y;
// Hand offset in camera coordinates ...
handOffset = hands[h].controller.getAbsTranslation();
handOffset = {
x: hands[h].zeroPosition.x - handOffset.x,
y: hands[h].zeroPosition.y - handOffset.z,
z: hands[h].zeroPosition.z + handOffset.y
};
handOffset.z = -handOffset.z;
// TODO: Leap Motion controller's right-hand roll calculation only works if physical hand is upside down.
// Approximate fix is to add a fudge factor.
if (h === 1 && isWindows) {
handRoll = handRoll + 0.6 * PI;
}
// Hand offset in world coordinates ...
cameraOrientation = Camera.getOrientation();
handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset));
// Hand position and orientation ...
if (h === 0) {
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
// Hand offset in avatar coordinates ...
inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation);
handOffset = Vec3.subtract(handOffset, MyAvatar.position);
handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset);
handOffset.z = -handOffset.z;
handOffset.x = -handOffset.x;
// Hand rotation in camera coordinates ...
// TODO: 2.0* scale factors should not be necessary; Leap Motion controller code needs investigating.
handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
wristAbsRotation = wrists[h].controller.getAbsRotation();
handPitch = 2.0 * wristAbsRotation.x - PI / 2.0;
handYaw = 2.0 * -wristAbsRotation.y;
// TODO: Roll values only work if hand is upside down; Leap Motion controller code needs investigating.
handRoll = PI + handRoll;
if (h === 0) {
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
} else {
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
}
// Hand rotation in avatar coordinates ...
cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation);
handRotation = Quat.multiply(inverseAvatarOrientation, handRotation);
} else {
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
handOffset = hands[h].controller.getAbsTranslation();
handOffset = {
x: -handOffset.x,
y: hands[h].zeroPosition.y + handOffset.y,
z: hands[h].zeroPosition.z - handOffset.z
};
// TODO: 2.0* scale factors should not be necessary; Leap Motion controller code needs investigating.
handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
wristAbsRotation = wrists[h].controller.getAbsRotation();
handPitch = 2.0 * -wristAbsRotation.x;
handYaw = 2.0 * wristAbsRotation.y;
// TODO: Leap Motion controller's right-hand roll calculation only works if physical hand is upside down.
// Approximate fix is to add a fudge factor.
if (h === 1 && isWindows) {
handRoll = handRoll + 0.6 * PI;
}
// Hand position and orientation ...
if (h === 0) {
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
} else {
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
}
}
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
// Finger joints ...

View file

@ -599,8 +599,11 @@ void Application::paintGL() {
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
_myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
if (!OculusManager::isConnected()) {
_myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
}
// OculusManager::display() updates camera position and rotation a bit further on.
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
//Note, the camera distance is set in Camera::setMode() so we dont have to do it here.
@ -630,7 +633,9 @@ void Application::paintGL() {
}
// Update camera position
_myCamera.update( 1.f/_fps );
if (!OculusManager::isConnected()) {
_myCamera.update(1.f / _fps);
}
// Note: whichCamera is used to pick between the normal camera myCamera for our
// main camera, vs, an alternate camera. The alternate camera we support right now
@ -640,7 +645,7 @@ void Application::paintGL() {
// Why have two cameras? Well, one reason is that because in the case of the renderViewFrustum()
// code, we want to keep the state of "myCamera" intact, so we can render what the view frustum of
// myCamera is. But we also want to do meaningful camera transforms on OpenGL for the offset camera
Camera whichCamera = _myCamera;
Camera* whichCamera = &_myCamera;
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) {
@ -654,7 +659,7 @@ void Application::paintGL() {
_viewFrustumOffsetCamera.setDistance(viewFrustumOffset.distance);
_viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation
_viewFrustumOffsetCamera.update(1.f/_fps);
whichCamera = _viewFrustumOffsetCamera;
whichCamera = &_viewFrustumOffsetCamera;
}
if (Menu::getInstance()->getShadowsEnabled()) {
@ -667,15 +672,16 @@ void Application::paintGL() {
glClear(GL_COLOR_BUFFER_BIT);
//When in mirror mode, use camera rotation. Otherwise, use body rotation
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) {
OculusManager::display(whichCamera.getRotation(), whichCamera.getPosition(), whichCamera);
if (whichCamera->getMode() == CAMERA_MODE_MIRROR) {
OculusManager::display(whichCamera->getRotation(), whichCamera->getPosition(), *whichCamera);
} else {
OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), whichCamera);
OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), *whichCamera);
}
_myCamera.update(1.f / _fps);
} else if (TV3DManager::isConnected()) {
TV3DManager::display(whichCamera);
TV3DManager::display(*whichCamera);
} else {
_glowEffect.prepare();
@ -683,7 +689,7 @@ void Application::paintGL() {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
displaySide(whichCamera);
displaySide(*whichCamera);
glPopMatrix();
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {

View file

@ -453,6 +453,9 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false);
QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
QMenu* networkMenu = developerMenu->addMenu("Network");
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
addCheckableActionToQMenuAndActionHash(networkMenu,

View file

@ -403,6 +403,7 @@ namespace MenuOption {
const QString IncreaseAvatarSize = "Increase Avatar Size";
const QString IncreaseVoxelSize = "Increase Voxel Size";
const QString KeyboardMotorControl = "Enable Keyboard Motor Control";
const QString LeapMotionOnHMD = "Leap Motion on HMD";
const QString LoadScript = "Open and Run Script File...";
const QString LoadScriptURL = "Open and Run Script from URL...";
const QString LodTools = "LOD Tools";

View file

@ -8,8 +8,9 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SharedUtil.h"
#include "Leapmotion.h"
#include "Menu.h"
#include "SharedUtil.h"
const int PALMROOT_NUM_JOINTS = 3;
const int FINGER_NUM_JOINTS = 4;
@ -101,6 +102,12 @@ Leapmotion::Leapmotion() :
}
}
}
#ifdef HAVE_LEAPMOTION
if (Menu::getInstance()->isOptionChecked(MenuOption::LeapMotionOnHMD)) {
_controller.setPolicyFlags(Leap::Controller::POLICY_OPTIMIZE_HMD);
}
#endif
}
Leapmotion::~Leapmotion() {

View file

@ -412,6 +412,10 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
glBindTexture(GL_TEXTURE_2D, 0);
// Update camera for use by rest of Interface.
whichCamera.setTargetPosition((_leftEyePosition + _rightEyePosition) / 2.f);
whichCamera.setTargetRotation(_camera->getTargetRotation());
#endif
}

View file

@ -265,7 +265,11 @@ QScriptValue WindowScriptingInterface::doPeekNonBlockingFormResult(QScriptValue
_form.setProperty(i, item);
} else if (item.property("options").isArray()) {
c += 1;
item.setProperty("value", _combos.at(c)->currentText());
item.setProperty("value",
_combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ?
item.property("options").property(_combos.at(c)->currentIndex()) :
array.engine()->undefinedValue()
);
_form.setProperty(i, item);
} else {
e += 1;
@ -318,7 +322,11 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
_form.setProperty(i, item);
} else if (item.property("options").isArray()) {
c += 1;
item.setProperty("value", _combos.at(c)->currentText());
item.setProperty("value",
_combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ?
item.property("options").property(_combos.at(c)->currentIndex()) :
array.engine()->undefinedValue()
);
_form.setProperty(i, item);
} else {
e += 1;
@ -349,6 +357,7 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
_form = QScriptValue();
_edits.clear();
_directories.clear();
_combos.clear();
array = _form;
return (_formResult == QDialog::Accepted);
@ -391,8 +400,12 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
form.setProperty(i, item);
} else if (item.property("options").isArray()) {
c += 1;
item.setProperty("value", _combos.at(c)->currentText());
_form.setProperty(i, item);
item.setProperty("value",
_combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ?
item.property("options").property(_combos.at(c)->currentIndex()) :
form.engine()->undefinedValue()
);
form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
@ -418,6 +431,7 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
}
delete editDialog;
_combos.clear();
_edits.clear();
_directories.clear();
return (result == QDialog::Accepted);
@ -498,9 +512,9 @@ QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue
} else if (item.property("options").isArray()) {
QComboBox* combo = new QComboBox();
combo->setMinimumWidth(200);
QStringList options = item.property("options").toVariant().toStringList();
for (QStringList::const_iterator it = options.begin(); it != options.end(); it += 1) {
combo->addItem(*it);
qint32 options_count = item.property("options").property("length").toInt32();
for (qint32 i = 0; i < options_count; i++) {
combo->addItem(item.property("options").property(i).toString());
}
_combos.push_back(combo);
formLayout->addRow(new QLabel(item.property("label").toString()), combo);

View file

@ -96,6 +96,7 @@ void AudioInjector::injectAudio() {
packetStream << radius;
// pack 255 for attenuation byte
int volumeOptionOffset = injectAudioPacket.size();
quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume();
packetStream << volume;
@ -118,6 +119,8 @@ void AudioInjector::injectAudio() {
memcpy(injectAudioPacket.data() + orientationOptionOffset,
&_options.getOrientation(),
sizeof(_options.getOrientation()));
volume = MAX_INJECTOR_VOLUME * _options.getVolume();
memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume));
// resize the QByteArray to the right size
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);

View file

@ -652,13 +652,25 @@ void AvatarData::startPlaying() {
_player->startPlaying();
}
void AvatarData::setPlayerFrame(int frame) {
void AvatarData::setPlayerVolume(float volume) {
if (_player) {
_player->setVolume(volume);
}
}
void AvatarData::setPlayerAudioOffset(int audioOffset) {
if (_player) {
_player->setAudioOffset(audioOffset);
}
}
void AvatarData::setPlayerFrame(unsigned int frame) {
if (_player) {
_player->setCurrentFrame(frame);
}
}
void AvatarData::setPlayerTime(qint64 time) {
void AvatarData::setPlayerTime(unsigned int time) {
if (_player) {
_player->setCurrentTime(time);
}

View file

@ -100,8 +100,6 @@ enum KeyState {
DELETE_KEY_DOWN
};
const glm::vec3 vec3Zero(0.0f);
class QDataStream;
class AttachmentData;
@ -304,8 +302,10 @@ public slots:
void loadRecording(QString filename);
void startPlaying();
void setPlayerFrame(int frame);
void setPlayerTime(qint64 time);
void setPlayerVolume(float volume);
void setPlayerAudioOffset(int audioOffset);
void setPlayerFrame(unsigned int frame);
void setPlayerTime(unsigned int time);
void setPlayFromCurrentLocation(bool playFromCurrentLocation);
void setPlayerLoop(bool loop);
void setPlayerUseDisplayName(bool useDisplayName);

View file

@ -17,13 +17,16 @@
#include "AvatarData.h"
#include "Player.h"
static const int INVALID_FRAME = -1;
Player::Player(AvatarData* avatar) :
_recording(new Recording()),
_currentFrame(-1),
_frameInterpolationFactor(0.0f),
_pausedFrame(-1),
_timerOffset(0),
_avatar(avatar),
_recording(new Recording()),
_currentFrame(INVALID_FRAME),
_frameInterpolationFactor(0.0f),
_pausedFrame(INVALID_FRAME),
_timerOffset(0),
_audioOffset(0),
_audioThread(NULL),
_playFromCurrentPosition(true),
_loop(false),
@ -33,8 +36,6 @@ Player::Player(AvatarData* avatar) :
_useSkeletonURL(true)
{
_timer.invalidate();
_options.setLoop(false);
_options.setVolume(1.0f);
}
bool Player::isPlaying() const {
@ -42,7 +43,7 @@ bool Player::isPlaying() const {
}
bool Player::isPaused() const {
return (_pausedFrame != -1);
return (_pausedFrame != INVALID_FRAME);
}
qint64 Player::elapsed() const {
@ -122,7 +123,7 @@ void Player::startPlaying() {
_timer.start();
setCurrentFrame(_pausedFrame);
_pausedFrame = -1;
_pausedFrame = INVALID_FRAME;
}
}
@ -130,7 +131,7 @@ void Player::stopPlaying() {
if (!isPlaying()) {
return;
}
_pausedFrame = -1;
_pausedFrame = INVALID_FRAME;
_timer.invalidate();
cleanupAudioThread();
_avatar->clearJointsData();
@ -201,12 +202,12 @@ void Player::loadFromFile(const QString& file) {
}
readRecordingFromFile(_recording, file);
_pausedFrame = -1;
_pausedFrame = INVALID_FRAME;
}
void Player::loadRecording(RecordingPointer recording) {
_recording = recording;
_pausedFrame = -1;
_pausedFrame = INVALID_FRAME;
}
void Player::play() {
@ -253,6 +254,9 @@ void Player::play() {
HeadData* head = const_cast<HeadData*>(_avatar->getHeadData());
if (head) {
// Make sure fake faceshift connection doesn't get turned off
_avatar->setForceFaceshiftConnected(true);
QVector<float> blendCoef(currentFrame.getBlendshapeCoefficients().size());
for (int i = 0; i < currentFrame.getBlendshapeCoefficients().size(); ++i) {
blendCoef[i] = glm::mix(currentFrame.getBlendshapeCoefficients()[i],
@ -293,8 +297,8 @@ void Player::play() {
_injector->setOptions(_options);
}
void Player::setCurrentFrame(int currentFrame) {
if (_recording && (currentFrame < 0 || currentFrame >= _recording->getFrameNumber())) {
void Player::setCurrentFrame(unsigned int currentFrame) {
if (_recording && currentFrame >= _recording->getFrameNumber()) {
stopPlaying();
return;
}
@ -306,12 +310,12 @@ void Player::setCurrentFrame(int currentFrame) {
_timer.start();
setAudionInjectorPosition();
} else {
_pausedFrame = currentFrame;
_pausedFrame = _currentFrame;
}
}
void Player::setCurrentTime(qint64 currentTime) {
if (currentTime < 0 || currentTime >= _recording->getLength()) {
void Player::setCurrentTime(unsigned int currentTime) {
if (currentTime >= _recording->getLength()) {
stopPlaying();
return;
}
@ -355,6 +359,18 @@ void Player::setCurrentTime(qint64 currentTime) {
}
}
void Player::setVolume(float volume) {
_options.setVolume(volume);
if (_injector) {
_injector->setOptions(_options);
}
qDebug() << "New volume: " << volume;
}
void Player::setAudioOffset(int audioOffset) {
_audioOffset = audioOffset;
}
void Player::setAudionInjectorPosition() {
int MSEC_PER_SEC = 1000;
int SAMPLE_SIZE = 2; // 16 bits
@ -370,14 +386,19 @@ void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) {
bool Player::computeCurrentFrame() {
if (!isPlaying()) {
_currentFrame = -1;
_currentFrame = INVALID_FRAME;
return false;
}
if (_currentFrame < 0) {
_currentFrame = 0;
}
qint64 elapsed = Player::elapsed();
quint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength());
while(_currentFrame >= 0 &&
_recording->getFrameTimestamp(_currentFrame) > elapsed) {
--_currentFrame;
}
while (_currentFrame < _recording->getFrameNumber() &&
_recording->getFrameTimestamp(_currentFrame) < elapsed) {
++_currentFrame;

View file

@ -44,8 +44,11 @@ public slots:
void loadRecording(RecordingPointer recording);
void play();
void setCurrentFrame(int currentFrame);
void setCurrentTime(qint64 currentTime);
void setCurrentFrame(unsigned int currentFrame);
void setCurrentTime(unsigned int currentTime);
void setVolume(float volume);
void setAudioOffset(int audioOffset);
void setPlayFromCurrentLocation(bool playFromCurrentPosition);
void setLoop(bool loop) { _loop = loop; }
@ -61,19 +64,20 @@ private:
void setAudionInjectorPosition();
bool computeCurrentFrame();
QElapsedTimer _timer;
AvatarData* _avatar;
RecordingPointer _recording;
int _currentFrame;
float _frameInterpolationFactor;
int _pausedFrame;
qint64 _timerOffset;
QElapsedTimer _timer;
int _timerOffset;
int _audioOffset;
QThread* _audioThread;
QSharedPointer<AudioInjector> _injector;
AudioInjectorOptions _options;
AvatarData* _avatar;
QThread* _audioThread;
RecordingContext _currentContext;
bool _playFromCurrentPosition;
bool _loop;

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <memory>
#include <QtCore/QDataStream>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
@ -25,9 +27,14 @@
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
AccountManager& AccountManager::getInstance() {
static AccountManager sharedInstance;
return sharedInstance;
AccountManager& AccountManager::getInstance(bool forceReset) {
static std::auto_ptr<AccountManager> sharedInstance(new AccountManager());
if (forceReset) {
sharedInstance.reset(new AccountManager());
}
return *sharedInstance;
}
Q_DECLARE_METATYPE(OAuthAccessToken)

View file

@ -42,7 +42,7 @@ const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
class AccountManager : public QObject {
Q_OBJECT
public:
static AccountManager& getInstance();
static AccountManager& getInstance(bool forceReset = false);
void authenticatedRequest(const QString& path,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,

View file

@ -35,29 +35,32 @@ const char SOLO_NODE_TYPES[2] = {
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io");
LimitedNodeList* LimitedNodeList::_sharedInstance = NULL;
std::auto_ptr<LimitedNodeList> LimitedNodeList::_sharedInstance;
LimitedNodeList* LimitedNodeList::createInstance(unsigned short socketListenPort, unsigned short dtlsPort) {
if (!_sharedInstance) {
NodeType::init();
NodeType::init();
if (_sharedInstance.get()) {
qDebug() << "LimitedNodeList called with existing instance." <<
"Releasing auto_ptr, deleting existing instance and creating a new one.";
_sharedInstance = new LimitedNodeList(socketListenPort, dtlsPort);
// register the SharedNodePointer meta-type for signals/slots
qRegisterMetaType<SharedNodePointer>();
} else {
qDebug("LimitedNodeList createInstance called with existing instance.");
delete _sharedInstance.release();
}
_sharedInstance = std::auto_ptr<LimitedNodeList>(new LimitedNodeList(socketListenPort, dtlsPort));
// register the SharedNodePointer meta-type for signals/slots
qRegisterMetaType<SharedNodePointer>();
return _sharedInstance;
return _sharedInstance.get();
}
LimitedNodeList* LimitedNodeList::getInstance() {
if (!_sharedInstance) {
if (!_sharedInstance.get()) {
qDebug("LimitedNodeList getInstance called before call to createInstance. Returning NULL pointer.");
}
return _sharedInstance;
return _sharedInstance.get();
}

View file

@ -14,6 +14,7 @@
#include <stdint.h>
#include <iterator>
#include <memory>
#ifndef _WIN32
#include <unistd.h> // not on windows, not needed for mac or windows
@ -118,7 +119,7 @@ signals:
void nodeKilled(SharedNodePointer);
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
protected:
static LimitedNodeList* _sharedInstance;
static std::auto_ptr<LimitedNodeList> _sharedInstance;
LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort);
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton

View file

@ -24,30 +24,31 @@
#include "SharedUtil.h"
#include "UUID.h"
NodeList* NodeList::_sharedInstance = NULL;
NodeList* NodeList::createInstance(char ownerType, unsigned short socketListenPort, unsigned short dtlsPort) {
if (!_sharedInstance) {
NodeType::init();
NodeType::init();
if (_sharedInstance.get()) {
qDebug() << "NodeList called with existing instance." <<
"Releasing auto_ptr, deleting existing instance and creating a new one.";
_sharedInstance = new NodeList(ownerType, socketListenPort, dtlsPort);
LimitedNodeList::_sharedInstance = _sharedInstance;
// register the SharedNodePointer meta-type for signals/slots
qRegisterMetaType<SharedNodePointer>();
} else {
qDebug("NodeList createInstance called with existing instance.");
delete _sharedInstance.release();
}
return _sharedInstance;
_sharedInstance = std::auto_ptr<LimitedNodeList>(new NodeList(ownerType, socketListenPort, dtlsPort));
// register the SharedNodePointer meta-type for signals/slots
qRegisterMetaType<SharedNodePointer>();
return static_cast<NodeList*>(_sharedInstance.get());
}
NodeList* NodeList::getInstance() {
if (!_sharedInstance) {
if (!_sharedInstance.get()) {
qDebug("NodeList getInstance called before call to createInstance. Returning NULL pointer.");
}
return _sharedInstance;
return static_cast<NodeList*>(_sharedInstance.get());
}
NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :

View file

@ -84,8 +84,6 @@ public slots:
signals:
void limitOfSilentDomainCheckInsReached();
private:
static NodeList* _sharedInstance;
NodeList(char ownerType, unsigned short socketListenPort, unsigned short dtlsListenPort);
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton