mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 13:18:38 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into 20061
This commit is contained in:
commit
afc27b8a8b
47 changed files with 1103 additions and 655 deletions
|
@ -62,6 +62,7 @@
|
||||||
#include "AudioMixer.h"
|
#include "AudioMixer.h"
|
||||||
|
|
||||||
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
|
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
|
||||||
|
const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18;
|
||||||
|
|
||||||
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
||||||
|
|
||||||
|
@ -82,6 +83,7 @@ AudioMixer::AudioMixer(const QByteArray& packet) :
|
||||||
_trailingSleepRatio(1.0f),
|
_trailingSleepRatio(1.0f),
|
||||||
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
|
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
|
||||||
_performanceThrottlingRatio(0.0f),
|
_performanceThrottlingRatio(0.0f),
|
||||||
|
_attenuationPerDoublingInDistance(DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE),
|
||||||
_numStatFrames(0),
|
_numStatFrames(0),
|
||||||
_sumListeners(0),
|
_sumListeners(0),
|
||||||
_sumMixes(0),
|
_sumMixes(0),
|
||||||
|
@ -104,7 +106,6 @@ AudioMixer::~AudioMixer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f;
|
const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f;
|
||||||
const float ATTENUATION_AMOUNT_PER_DOUBLING_IN_DISTANCE = 0.18f;
|
|
||||||
const float RADIUS_OF_HEAD = 0.076f;
|
const float RADIUS_OF_HEAD = 0.076f;
|
||||||
|
|
||||||
int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData,
|
int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData,
|
||||||
|
@ -210,7 +211,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l
|
||||||
if (shouldDistanceAttenuate && (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE)) {
|
if (shouldDistanceAttenuate && (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE)) {
|
||||||
// calculate the distance coefficient using the distance to this node
|
// calculate the distance coefficient using the distance to this node
|
||||||
float distanceCoefficient = 1 - (logf(distanceBetween / ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f)
|
float distanceCoefficient = 1 - (logf(distanceBetween / ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f)
|
||||||
* ATTENUATION_AMOUNT_PER_DOUBLING_IN_DISTANCE);
|
* _attenuationPerDoublingInDistance);
|
||||||
|
|
||||||
if (distanceCoefficient < 0) {
|
if (distanceCoefficient < 0) {
|
||||||
distanceCoefficient = 0;
|
distanceCoefficient = 0;
|
||||||
|
@ -643,7 +644,7 @@ void AudioMixer::run() {
|
||||||
QJsonObject audioGroupObject = settingsObject[AUDIO_GROUP_KEY].toObject();
|
QJsonObject audioGroupObject = settingsObject[AUDIO_GROUP_KEY].toObject();
|
||||||
|
|
||||||
// check the payload to see if we have asked for dynamicJitterBuffer support
|
// 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();
|
_streamSettings._dynamicJitterBuffers = audioGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
|
||||||
if (_streamSettings._dynamicJitterBuffers) {
|
if (_streamSettings._dynamicJitterBuffers) {
|
||||||
qDebug() << "Enable dynamic jitter buffers.";
|
qDebug() << "Enable dynamic jitter buffers.";
|
||||||
|
@ -652,21 +653,21 @@ void AudioMixer::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok;
|
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);
|
_streamSettings._staticDesiredJitterBufferFrames = audioGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
_streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES;
|
_streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES;
|
||||||
}
|
}
|
||||||
qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames;
|
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);
|
_streamSettings._maxFramesOverDesired = audioGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
_streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED;
|
_streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED;
|
||||||
}
|
}
|
||||||
qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired;
|
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();
|
_streamSettings._useStDevForJitterCalc = audioGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool();
|
||||||
if (_streamSettings._useStDevForJitterCalc) {
|
if (_streamSettings._useStDevForJitterCalc) {
|
||||||
qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled";
|
qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled";
|
||||||
|
@ -674,28 +675,28 @@ void AudioMixer::run() {
|
||||||
qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled";
|
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);
|
_streamSettings._windowStarveThreshold = audioGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
_streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD;
|
_streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD;
|
||||||
}
|
}
|
||||||
qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold;
|
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);
|
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES;
|
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES;
|
||||||
}
|
}
|
||||||
qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds";
|
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);
|
_streamSettings._windowSecondsForDesiredReduction = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
_streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION;
|
_streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION;
|
||||||
}
|
}
|
||||||
qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds";
|
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();
|
_streamSettings._repetitionWithFade = audioGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool();
|
||||||
if (_streamSettings._repetitionWithFade) {
|
if (_streamSettings._repetitionWithFade) {
|
||||||
qDebug() << "Repetition with fade enabled";
|
qDebug() << "Repetition with fade enabled";
|
||||||
|
@ -703,13 +704,13 @@ void AudioMixer::run() {
|
||||||
qDebug() << "Repetition with fade disabled";
|
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();
|
_printStreamStats = audioGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool();
|
||||||
if (_printStreamStats) {
|
if (_printStreamStats) {
|
||||||
qDebug() << "Stream stats will be printed to stdout";
|
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()) {
|
if (audioGroupObject[FILTER_KEY].isBool()) {
|
||||||
_enableFilter = audioGroupObject[FILTER_KEY].toBool();
|
_enableFilter = audioGroupObject[FILTER_KEY].toBool();
|
||||||
}
|
}
|
||||||
|
@ -717,7 +718,7 @@ void AudioMixer::run() {
|
||||||
qDebug() << "Filter enabled";
|
qDebug() << "Filter enabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString UNATTENUATED_ZONE_KEY = "unattenuated-zone";
|
const QString UNATTENUATED_ZONE_KEY = "unattenuated_zone";
|
||||||
|
|
||||||
QString unattenuatedZoneString = audioGroupObject[UNATTENUATED_ZONE_KEY].toString();
|
QString unattenuatedZoneString = audioGroupObject[UNATTENUATED_ZONE_KEY].toString();
|
||||||
if (!unattenuatedZoneString.isEmpty()) {
|
if (!unattenuatedZoneString.isEmpty()) {
|
||||||
|
@ -740,6 +741,16 @@ void AudioMixer::run() {
|
||||||
qDebug() << "Buffers inside this zone will not be attenuated inside a box with center at"
|
qDebug() << "Buffers inside this zone will not be attenuated inside a box with center at"
|
||||||
<< QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z);
|
<< 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";
|
||||||
|
if (audioGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].isString()) {
|
||||||
|
bool ok = false;
|
||||||
|
float attenuation = audioGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok);
|
||||||
|
if (ok) {
|
||||||
|
_attenuationPerDoublingInDistance = attenuation;
|
||||||
|
qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextFrame = 0;
|
int nextFrame = 0;
|
||||||
|
|
|
@ -69,6 +69,7 @@ private:
|
||||||
float _trailingSleepRatio;
|
float _trailingSleepRatio;
|
||||||
float _minAudibilityThreshold;
|
float _minAudibilityThreshold;
|
||||||
float _performanceThrottlingRatio;
|
float _performanceThrottlingRatio;
|
||||||
|
float _attenuationPerDoublingInDistance;
|
||||||
int _numStatFrames;
|
int _numStatFrames;
|
||||||
int _sumListeners;
|
int _sumListeners;
|
||||||
int _sumMixes;
|
int _sumMixes;
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
"label": "Metaverse Registration",
|
"label": "Metaverse Registration",
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "access-token",
|
"name": "access_token",
|
||||||
"label": "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",
|
"name": "id",
|
||||||
|
@ -20,12 +20,12 @@
|
||||||
"label": "Security",
|
"label": "Security",
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "http-username",
|
"name": "http_username",
|
||||||
"label": "HTTP Username",
|
"label": "HTTP Username",
|
||||||
"help": "Username used for basic HTTP authentication."
|
"help": "Username used for basic HTTP authentication."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "http-password",
|
"name": "http_password",
|
||||||
"label": "HTTP Password",
|
"label": "HTTP Password",
|
||||||
"type": "password",
|
"type": "password",
|
||||||
"help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.",
|
"help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.",
|
||||||
|
@ -39,20 +39,28 @@
|
||||||
"assignment-types": [0],
|
"assignment-types": [0],
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "enable-filter",
|
"name": "enable_filter",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"label": "Enable Positional Filter",
|
"label": "Enable Positional Filter",
|
||||||
"help": "positional audio stream uses lowpass filter",
|
"help": "positional audio stream uses lowpass filter",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "unattenuated-zone",
|
"name": "unattenuated_zone",
|
||||||
"label": "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)",
|
"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"
|
"placeholder": "no zone"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dynamic-jitter-buffer",
|
"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",
|
||||||
|
"default": "0.18",
|
||||||
|
"advanced": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dynamic_jitter_buffer",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"label": "Dynamic Jitter Buffers",
|
"label": "Dynamic Jitter Buffers",
|
||||||
"help": "dynamically buffer client audio based on perceived jitter in packet receipt timing",
|
"help": "dynamically buffer client audio based on perceived jitter in packet receipt timing",
|
||||||
|
@ -60,7 +68,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "static-desired-jitter-buffer-frames",
|
"name": "static_desired_jitter_buffer_frames",
|
||||||
"label": "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",
|
"help": "If dynamic jitter buffers is disabled, this determines the target number of frames maintained by the AudioMixer's jitter buffers",
|
||||||
"placeholder": "1",
|
"placeholder": "1",
|
||||||
|
@ -68,7 +76,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "max-frames-over-desired",
|
"name": "max_frames_over_desired",
|
||||||
"label": "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",
|
"help": "The highest number of frames an AudioMixer's ringbuffer can exceed the desired jitter buffer frames by",
|
||||||
"placeholder": "10",
|
"placeholder": "10",
|
||||||
|
@ -76,7 +84,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "use-stdev-for-desired-calc",
|
"name": "use_stdev_for_desired_calc",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"label": "Use Stdev for Desired Jitter Frames Calc:",
|
"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)",
|
"help": "use Philip's method (stdev of timegaps) to calculate desired jitter frames (otherwise Fred's max timegap method is used)",
|
||||||
|
@ -84,7 +92,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "window-starve-threshold",
|
"name": "window_starve_threshold",
|
||||||
"label": "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.",
|
"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",
|
"placeholder": "3",
|
||||||
|
@ -92,7 +100,7 @@
|
||||||
"advanced": true
|
"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:",
|
"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.",
|
"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",
|
"placeholder": "50",
|
||||||
|
@ -100,7 +108,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "window-seconds-for-desired-reduction",
|
"name": "window_seconds_for_desired_reduction",
|
||||||
"label": "Timegaps Window (B) Seconds:",
|
"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.",
|
"help": "Window B contains a history of timegaps. Its max timegap is used as a ceiling for the desired jitter frames value.",
|
||||||
"placeholder": "10",
|
"placeholder": "10",
|
||||||
|
@ -108,7 +116,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "repetition-with-fade",
|
"name": "repetition_with_fade",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"label": "Repetition with Fade:",
|
"label": "Repetition with Fade:",
|
||||||
"help": "dropped frames and mixing during starves repeat the last frame, eventually fading to silence",
|
"help": "dropped frames and mixing during starves repeat the last frame, eventually fading to silence",
|
||||||
|
@ -116,7 +124,7 @@
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "I-print-stream-stats",
|
"name": "print_stream_stats",
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"label": "Print Stream Stats:",
|
"label": "Print Stream Stats:",
|
||||||
"help": "audio upstream and downstream stats of each agent printed to audio-mixer stdout",
|
"help": "audio upstream and downstream stats of each agent printed to audio-mixer stdout",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<pre id='editor' style='font-size: 14px;'><!--#include file="placeholder.js"--></object></pre>
|
<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/ace/ace.js' type='text/javascript'></script>
|
||||||
<script src='js/assignment.js' type='text/javascript'></script>
|
<script src='js/assignment.js' type='text/javascript'></script>
|
||||||
<div class='big-button' id='deploy-button'>
|
<div class='big-button' id='deploy-button'>
|
||||||
|
|
|
@ -52,6 +52,10 @@ span.port {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.locked {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
.advanced-setting {
|
.advanced-setting {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
</div>
|
</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/bootstrap.min.js'></script>
|
||||||
<script src='/js/domain-server.js'></script>
|
<script src='/js/domain-server.js'></script>
|
6
domain-server/resources/web/js/bootbox.min.js
vendored
Normal file
6
domain-server/resources/web/js/bootbox.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -3,8 +3,8 @@ var Settings = {
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewHelpers = {
|
var viewHelpers = {
|
||||||
getFormGroup: function(groupName, setting, values, isAdvanced) {
|
getFormGroup: function(groupName, setting, values, isAdvanced, isLocked) {
|
||||||
setting_id = groupName + "_" + setting.name
|
setting_name = groupName + "." + setting.name
|
||||||
|
|
||||||
form_group = "<div class='form-group" + (isAdvanced ? " advanced-setting" : "") + "'>"
|
form_group = "<div class='form-group" + (isAdvanced ? " advanced-setting" : "") + "'>"
|
||||||
|
|
||||||
|
@ -16,24 +16,29 @@ var viewHelpers = {
|
||||||
setting_value = ""
|
setting_value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label_class = 'control-label'
|
||||||
|
if (isLocked) {
|
||||||
|
label_class += ' locked'
|
||||||
|
}
|
||||||
|
|
||||||
if (setting.type === 'checkbox') {
|
if (setting.type === 'checkbox') {
|
||||||
form_group += "<label class='control-label'>" + setting.label + "</label>"
|
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||||
form_group += "<div class='checkbox'>"
|
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
|
||||||
form_group += "<label for='" + setting_id + "'>"
|
form_group += "<label for='" + setting_name + "'>"
|
||||||
form_group += "<input type='checkbox' id='" + setting_id + "' " + (setting_value ? "checked" : "") + "/>"
|
form_group += "<input type='checkbox' name='" + setting_name + "' " +
|
||||||
|
(setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
|
||||||
form_group += " " + setting.help + "</label>";
|
form_group += " " + setting.help + "</label>";
|
||||||
form_group += "</div>"
|
form_group += "</div>"
|
||||||
} else {
|
} else {
|
||||||
input_type = _.has(setting, 'type') ? setting.type : "text"
|
input_type = _.has(setting, 'type') ? setting.type : "text"
|
||||||
|
|
||||||
form_group += "<label for='" + setting_id + "' class='control-label'>" + setting.label + "</label>";
|
form_group += "<label for='" + setting_name + "' class='" + label_class + "'>" + setting.label + "</label>";
|
||||||
form_group += "<input type='" + input_type + "' class='form-control' id='" + setting_id +
|
form_group += "<input type='" + input_type + "' class='form-control' name='" + setting_name +
|
||||||
"' placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
|
"' placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
|
||||||
"' value='" + setting_value + "'/>"
|
"' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>"
|
||||||
form_group += "<span class='help-block'>" + setting.help + "</span>"
|
form_group += "<span class='help-block'>" + setting.help + "</span>"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
form_group += "</div>"
|
form_group += "</div>"
|
||||||
return form_group
|
return form_group
|
||||||
}
|
}
|
||||||
|
@ -82,6 +87,10 @@ $(document).ready(function(){
|
||||||
$(this).blur()
|
$(this).blur()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('#settings-form').on('click', '#choose-domain-btn', function(){
|
||||||
|
chooseFromHighFidelityDomains($(this))
|
||||||
|
})
|
||||||
|
|
||||||
var panelsSource = $('#panels-template').html()
|
var panelsSource = $('#panels-template').html()
|
||||||
Settings.panelsTemplate = _.template(panelsSource)
|
Settings.panelsTemplate = _.template(panelsSource)
|
||||||
|
|
||||||
|
@ -100,10 +109,24 @@ function reloadSettings() {
|
||||||
$('.nav-stacked').html(Settings.sidebarTemplate(data))
|
$('.nav-stacked').html(Settings.sidebarTemplate(data))
|
||||||
$('#panels').html(Settings.panelsTemplate(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!";
|
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
|
||||||
|
|
||||||
$('body').on('click', '.save-button', function(e){
|
$('body').on('click', '.save-button', function(e){
|
||||||
|
@ -113,7 +136,9 @@ $('body').on('click', '.save-button', function(e){
|
||||||
});
|
});
|
||||||
|
|
||||||
// grab a JSON representation of the form via form2js
|
// 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
|
// re-enable all inputs
|
||||||
$("input").each(function(){
|
$("input").each(function(){
|
||||||
|
@ -148,13 +173,13 @@ function badgeSidebarForDifferences(changedInput) {
|
||||||
var panelParentID = changedInput.closest('.panel').attr('id')
|
var panelParentID = changedInput.closest('.panel').attr('id')
|
||||||
|
|
||||||
// get a JSON representation of that section
|
// 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 panelJSON = rootJSON[panelParentID]
|
||||||
|
|
||||||
var badgeValue = 0
|
var badgeValue = 0
|
||||||
|
|
||||||
for (var setting in panelJSON) {
|
for (var setting in panelJSON) {
|
||||||
if (panelJSON[setting] != Settings.initialValues[panelParentID][ setting]) {
|
if (panelJSON[setting] != Settings.initialValues[panelParentID][setting]) {
|
||||||
badgeValue += 1
|
badgeValue += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +219,7 @@ function showRestartModal() {
|
||||||
|
|
||||||
function cleanupFormValues(node) {
|
function cleanupFormValues(node) {
|
||||||
if (node.type && node.type === 'checkbox') {
|
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 {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -207,3 +232,70 @@ function showAlertMessage(message, isSuccess) {
|
||||||
alertBox.html(message);
|
alertBox.html(message);
|
||||||
alertBox.fadeIn();
|
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"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,10 +38,12 @@
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
|
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
|
||||||
<% _.each(split_settings[0], function(setting) { %>
|
<% _.each(split_settings[0], function(setting) { %>
|
||||||
<%= getFormGroup(group.name, setting, values, false) %>
|
<%= getFormGroup(group.name, setting, values, false,
|
||||||
|
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
<% _.each(split_settings[1], function(setting) { %>
|
<% _.each(split_settings[1], function(setting) { %>
|
||||||
<%= getFormGroup(group.name, setting, values, true) %>
|
<%= getFormGroup(group.name, setting, values, true,
|
||||||
|
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,6 +75,7 @@
|
||||||
|
|
||||||
<!--#include virtual="footer.html"-->
|
<!--#include virtual="footer.html"-->
|
||||||
<script src='/js/underscore-min.js'></script>
|
<script src='/js/underscore-min.js'></script>
|
||||||
|
<script src='/js/bootbox.min.js'></script>
|
||||||
<script src='/js/settings.js'></script>
|
<script src='/js/settings.js'></script>
|
||||||
<script src='/js/form2js.min.js'></script>
|
<script src='/js/form2js.min.js'></script>
|
||||||
<!--#include virtual="page-end.html"-->
|
<!--#include virtual="page-end.html"-->
|
|
@ -57,7 +57,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
setApplicationName("domain-server");
|
setApplicationName("domain-server");
|
||||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||||
|
|
||||||
_settingsManager.loadSettingsMap(arguments());
|
_settingsManager.setupConfigMap(arguments());
|
||||||
|
|
||||||
installNativeEventFilter(&_shutdownEventListener);
|
installNativeEventFilter(&_shutdownEventListener);
|
||||||
connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit()));
|
connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit()));
|
||||||
|
@ -81,6 +81,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
void DomainServer::restart() {
|
void DomainServer::restart() {
|
||||||
qDebug() << "domain-server is restarting.";
|
qDebug() << "domain-server is restarting.";
|
||||||
|
|
||||||
|
// make sure all static instances are reset
|
||||||
|
LimitedNodeList::getInstance()->reset();
|
||||||
|
AccountManager::getInstance(true);
|
||||||
|
|
||||||
exit(DomainServer::EXIT_CODE_REBOOT);
|
exit(DomainServer::EXIT_CODE_REBOOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1396,8 +1401,8 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
|
const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
|
||||||
const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
|
const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
|
||||||
const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
|
const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
|
||||||
const QString BASIC_AUTH_USERNAME_KEY_PATH = "security.http-username";
|
const QString BASIC_AUTH_USERNAME_KEY_PATH = "security.http_username";
|
||||||
const QString BASIC_AUTH_PASSWORD_KEY_PATH = "security.http-password";
|
const QString BASIC_AUTH_PASSWORD_KEY_PATH = "security.http_password";
|
||||||
|
|
||||||
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
|
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings
|
||||||
|
|
||||||
DomainServerSettingsManager::DomainServerSettingsManager() :
|
DomainServerSettingsManager::DomainServerSettingsManager() :
|
||||||
_descriptionArray(),
|
_descriptionArray(),
|
||||||
_settingsMap()
|
_configMap()
|
||||||
{
|
{
|
||||||
// load the description object from the settings description
|
// load the description object from the settings description
|
||||||
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
|
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
|
||||||
|
@ -37,11 +37,30 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
||||||
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
|
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::loadSettingsMap(const QStringList& argumentList) {
|
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||||
_settingsMap = HifiConfigVariantMap::mergeMasterConfigWithUserConfig(argumentList);
|
_configMap.loadMasterAndUserConfig(argumentList);
|
||||||
|
|
||||||
// figure out where we are supposed to persist our settings to
|
// for now we perform a temporary transition from http-username and http-password to http_username and http_password
|
||||||
_settingsFilepath = HifiConfigVariantMap::userConfigFilepath(argumentList);
|
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";
|
const QString SETTINGS_PATH = "/settings.json";
|
||||||
|
@ -76,7 +95,7 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
|
||||||
QJsonObject postedObject = postedDocument.object();
|
QJsonObject postedObject = postedDocument.object();
|
||||||
|
|
||||||
// we recurse one level deep below each group for the appropriate setting
|
// we recurse one level deep below each group for the appropriate setting
|
||||||
recurseJSONObjectAndOverwriteSettings(postedObject, _settingsMap, _descriptionArray);
|
recurseJSONObjectAndOverwriteSettings(postedObject, _configMap.getUserConfig(), _descriptionArray);
|
||||||
|
|
||||||
// store whatever the current _settingsMap is to file
|
// store whatever the current _settingsMap is to file
|
||||||
persistToFile();
|
persistToFile();
|
||||||
|
@ -94,10 +113,13 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
|
||||||
// setup a JSON Object with descriptions and non-omitted settings
|
// setup a JSON Object with descriptions and non-omitted settings
|
||||||
const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions";
|
const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions";
|
||||||
const QString SETTINGS_RESPONSE_VALUE_KEY = "values";
|
const QString SETTINGS_RESPONSE_VALUE_KEY = "values";
|
||||||
|
const QString SETTINGS_RESPONSE_LOCKED_VALUES_KEY = "locked";
|
||||||
|
|
||||||
QJsonObject rootObject;
|
QJsonObject rootObject;
|
||||||
rootObject[SETTINGS_RESPONSE_DESCRIPTION_KEY] = _descriptionArray;
|
rootObject[SETTINGS_RESPONSE_DESCRIPTION_KEY] = _descriptionArray;
|
||||||
rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true);
|
rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true);
|
||||||
|
rootObject[SETTINGS_RESPONSE_LOCKED_VALUES_KEY] = QJsonDocument::fromVariant(_configMap.getMasterConfig()).object();
|
||||||
|
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json");
|
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json");
|
||||||
}
|
}
|
||||||
|
@ -145,7 +167,8 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty
|
||||||
|
|
||||||
// we need to check if the settings map has a value for this setting
|
// we need to check if the settings map has a value for this setting
|
||||||
QVariant variantValue;
|
QVariant variantValue;
|
||||||
QVariant settingsMapGroupValue = _settingsMap.value(groupObject[DESCRIPTION_NAME_KEY].toString());
|
QVariant settingsMapGroupValue = _configMap.getMergedConfig()
|
||||||
|
.value(groupObject[DESCRIPTION_NAME_KEY].toString());
|
||||||
|
|
||||||
if (!settingsMapGroupValue.isNull()) {
|
if (!settingsMapGroupValue.isNull()) {
|
||||||
variantValue = settingsMapGroupValue.toMap().value(settingName);
|
variantValue = settingsMapGroupValue.toMap().value(settingName);
|
||||||
|
@ -239,23 +262,19 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray DomainServerSettingsManager::getJSONSettingsMap() const {
|
|
||||||
return QJsonDocument::fromVariant(_settingsMap).toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServerSettingsManager::persistToFile() {
|
void DomainServerSettingsManager::persistToFile() {
|
||||||
|
|
||||||
// make sure we have the dir the settings file is supposed to live in
|
// make sure we have the dir the settings file is supposed to live in
|
||||||
QFileInfo settingsFileInfo(_settingsFilepath);
|
QFileInfo settingsFileInfo(_configMap.getUserConfigFilename());
|
||||||
|
|
||||||
if (!settingsFileInfo.dir().exists()) {
|
if (!settingsFileInfo.dir().exists()) {
|
||||||
settingsFileInfo.dir().mkpath(".");
|
settingsFileInfo.dir().mkpath(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile settingsFile(_settingsFilepath);
|
QFile settingsFile(_configMap.getUserConfigFilename());
|
||||||
|
|
||||||
if (settingsFile.open(QIODevice::WriteOnly)) {
|
if (settingsFile.open(QIODevice::WriteOnly)) {
|
||||||
settingsFile.write(getJSONSettingsMap());
|
settingsFile.write(QJsonDocument::fromVariant(_configMap.getUserConfig()).toJson());
|
||||||
} else {
|
} else {
|
||||||
qCritical("Could not write to JSON settings file. Unable to persist settings.");
|
qCritical("Could not write to JSON settings file. Unable to persist settings.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QtCore/QJsonArray>
|
#include <QtCore/QJsonArray>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
|
|
||||||
|
#include <HifiConfigVariantMap.h>
|
||||||
#include <HTTPManager.h>
|
#include <HTTPManager.h>
|
||||||
|
|
||||||
class DomainServerSettingsManager : public QObject {
|
class DomainServerSettingsManager : public QObject {
|
||||||
|
@ -24,10 +25,9 @@ public:
|
||||||
bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
void loadSettingsMap(const QStringList& argumentList);
|
void setupConfigMap(const QStringList& argumentList);
|
||||||
|
|
||||||
QByteArray getJSONSettingsMap() const;
|
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
||||||
QVariantMap& getSettingsMap() { return _settingsMap; }
|
|
||||||
private:
|
private:
|
||||||
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
|
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
|
||||||
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant,
|
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant,
|
||||||
|
@ -35,8 +35,7 @@ private:
|
||||||
void persistToFile();
|
void persistToFile();
|
||||||
|
|
||||||
QJsonArray _descriptionArray;
|
QJsonArray _descriptionArray;
|
||||||
QVariantMap _settingsMap;
|
HifiConfigVariantMap _configMap;
|
||||||
QString _settingsFilepath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DomainServerSettingsManager_h
|
#endif // hifi_DomainServerSettingsManager_h
|
|
@ -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: "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: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
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" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||||
|
@ -2796,50 +2796,7 @@ var dimensionY;
|
||||||
var dimensionZ;
|
var dimensionZ;
|
||||||
var rescalePercentage;
|
var rescalePercentage;
|
||||||
|
|
||||||
function handeMenuEvent(menuItem) {
|
function showPropertiesForm() {
|
||||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
|
||||||
if (menuItem == "Delete") {
|
|
||||||
if (leftController.grabbing) {
|
|
||||||
print(" Delete Entity.... leftController.entityID="+ leftController.entityID);
|
|
||||||
Entities.deleteEntity(leftController.entityID);
|
|
||||||
leftController.grabbing = false;
|
|
||||||
if (glowedEntityID.id == leftController.entityID.id) {
|
|
||||||
glowedEntityID = { id: -1, isKnownID: false };
|
|
||||||
}
|
|
||||||
} else if (rightController.grabbing) {
|
|
||||||
print(" Delete Entity.... rightController.entityID="+ rightController.entityID);
|
|
||||||
Entities.deleteEntity(rightController.entityID);
|
|
||||||
rightController.grabbing = false;
|
|
||||||
if (glowedEntityID.id == rightController.entityID.id) {
|
|
||||||
glowedEntityID = { id: -1, isKnownID: false };
|
|
||||||
}
|
|
||||||
} else if (entitySelected) {
|
|
||||||
print(" Delete Entity.... selectedEntityID="+ selectedEntityID);
|
|
||||||
Entities.deleteEntity(selectedEntityID);
|
|
||||||
entitySelected = false;
|
|
||||||
if (glowedEntityID.id == selectedEntityID.id) {
|
|
||||||
glowedEntityID = { id: -1, isKnownID: false };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print(" Delete Entity.... not holding...");
|
|
||||||
}
|
|
||||||
} else if (menuItem == "Edit Properties...") {
|
|
||||||
editModelID = -1;
|
|
||||||
if (leftController.grabbing) {
|
|
||||||
print(" Edit Properties.... leftController.entityID="+ leftController.entityID);
|
|
||||||
editModelID = leftController.entityID;
|
|
||||||
} else if (rightController.grabbing) {
|
|
||||||
print(" Edit Properties.... rightController.entityID="+ rightController.entityID);
|
|
||||||
editModelID = rightController.entityID;
|
|
||||||
} else if (entitySelected) {
|
|
||||||
print(" Edit Properties.... selectedEntityID="+ selectedEntityID);
|
|
||||||
editModelID = selectedEntityID;
|
|
||||||
} else {
|
|
||||||
print(" Edit Properties.... not holding...");
|
|
||||||
}
|
|
||||||
if (editModelID != -1) {
|
|
||||||
print(" Edit Properties.... about to edit properties...");
|
|
||||||
|
|
||||||
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
|
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
|
||||||
var properties = propertiesForEditedEntity;
|
var properties = propertiesForEditedEntity;
|
||||||
|
|
||||||
|
@ -2959,6 +2916,79 @@ function handeMenuEvent(menuItem) {
|
||||||
|
|
||||||
editEntityFormArray = array;
|
editEntityFormArray = array;
|
||||||
Window.nonBlockingForm("Edit Properties", array);
|
Window.nonBlockingForm("Edit Properties", array);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handeMenuEvent(menuItem) {
|
||||||
|
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||||
|
if (menuItem == "Delete") {
|
||||||
|
if (leftController.grabbing) {
|
||||||
|
print(" Delete Entity.... leftController.entityID="+ leftController.entityID);
|
||||||
|
Entities.deleteEntity(leftController.entityID);
|
||||||
|
leftController.grabbing = false;
|
||||||
|
if (glowedEntityID.id == leftController.entityID.id) {
|
||||||
|
glowedEntityID = { id: -1, isKnownID: false };
|
||||||
|
}
|
||||||
|
} else if (rightController.grabbing) {
|
||||||
|
print(" Delete Entity.... rightController.entityID="+ rightController.entityID);
|
||||||
|
Entities.deleteEntity(rightController.entityID);
|
||||||
|
rightController.grabbing = false;
|
||||||
|
if (glowedEntityID.id == rightController.entityID.id) {
|
||||||
|
glowedEntityID = { id: -1, isKnownID: false };
|
||||||
|
}
|
||||||
|
} else if (entitySelected) {
|
||||||
|
print(" Delete Entity.... selectedEntityID="+ selectedEntityID);
|
||||||
|
Entities.deleteEntity(selectedEntityID);
|
||||||
|
entitySelected = false;
|
||||||
|
if (glowedEntityID.id == selectedEntityID.id) {
|
||||||
|
glowedEntityID = { id: -1, isKnownID: false };
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
print(" Edit Properties.... leftController.entityID="+ leftController.entityID);
|
||||||
|
editModelID = leftController.entityID;
|
||||||
|
} else if (rightController.grabbing) {
|
||||||
|
print(" Edit Properties.... rightController.entityID="+ rightController.entityID);
|
||||||
|
editModelID = rightController.entityID;
|
||||||
|
} else if (entitySelected) {
|
||||||
|
print(" Edit Properties.... selectedEntityID="+ selectedEntityID);
|
||||||
|
editModelID = selectedEntityID;
|
||||||
|
} else {
|
||||||
|
print(" Edit Properties.... not holding...");
|
||||||
|
}
|
||||||
|
if (editModelID != -1) {
|
||||||
|
print(" Edit Properties.... about to edit properties...");
|
||||||
|
showPropertiesForm(editModelID);
|
||||||
}
|
}
|
||||||
} else if (menuItem == "Paste Models") {
|
} else if (menuItem == "Paste Models") {
|
||||||
modelImporter.paste();
|
modelImporter.paste();
|
||||||
|
|
|
@ -43,7 +43,7 @@ var noFly = true;
|
||||||
var fixedWalkVelocity = true;
|
var fixedWalkVelocity = true;
|
||||||
|
|
||||||
//var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 };
|
//var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 };
|
||||||
var roomLimits = { xMin: 100.0, xMax: 206.5, zMin: 251.4, zMax: 269.5 };
|
var roomLimits = { xMin: -1.0, xMax: -1.0, zMin: -1.0, zMax: -1.0 };
|
||||||
|
|
||||||
function isInRoom(position) {
|
function isInRoom(position) {
|
||||||
var BUFFER = 2.0;
|
var BUFFER = 2.0;
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
|
|
||||||
var leapHands = (function () {
|
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,
|
wrists,
|
||||||
NUM_HANDS = 2, // 0 = left; 1 = right
|
NUM_HANDS = 2, // 0 = left; 1 = right
|
||||||
fingers,
|
fingers,
|
||||||
|
@ -188,8 +191,6 @@ var leapHands = (function () {
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
|
|
||||||
calibrationStatus = UNCALIBRATED;
|
|
||||||
|
|
||||||
// TODO: Leap Motion controller joint naming doesn't match up with skeleton joint naming; numbers are out by 1.
|
// TODO: Leap Motion controller joint naming doesn't match up with skeleton joint naming; numbers are out by 1.
|
||||||
|
|
||||||
hands = [
|
hands = [
|
||||||
|
@ -265,6 +266,20 @@ var leapHands = (function () {
|
||||||
{ jointName: "RightHandPinky3", controller: Controller.createInputController("Spatial", "joint_R_pinky4") }
|
{ 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() {
|
function moveHands() {
|
||||||
|
@ -278,7 +293,9 @@ var leapHands = (function () {
|
||||||
handYaw,
|
handYaw,
|
||||||
handRotation,
|
handRotation,
|
||||||
wristAbsRotation,
|
wristAbsRotation,
|
||||||
locRotation;
|
locRotation,
|
||||||
|
cameraOrientation,
|
||||||
|
inverseAvatarOrientation;
|
||||||
|
|
||||||
for (h = 0; h < NUM_HANDS; h += 1) {
|
for (h = 0; h < NUM_HANDS; h += 1) {
|
||||||
side = h === 0 ? -1.0 : 1.0;
|
side = h === 0 ? -1.0 : 1.0;
|
||||||
|
@ -291,6 +308,53 @@ var leapHands = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hand position ...
|
// Hand position ...
|
||||||
|
if (isOnHMD) {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Hand offset in world coordinates ...
|
||||||
|
cameraOrientation = Camera.getOrientation();
|
||||||
|
handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset));
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
|
||||||
handOffset = hands[h].controller.getAbsTranslation();
|
handOffset = hands[h].controller.getAbsTranslation();
|
||||||
handOffset = {
|
handOffset = {
|
||||||
x: -handOffset.x,
|
x: -handOffset.x,
|
||||||
|
@ -298,7 +362,7 @@ var leapHands = (function () {
|
||||||
z: hands[h].zeroPosition.z - handOffset.z
|
z: hands[h].zeroPosition.z - handOffset.z
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: 2.0* scale factor should not be necessary; Leap Motion controller code needs investigating.
|
// TODO: 2.0* scale factors should not be necessary; Leap Motion controller code needs investigating.
|
||||||
handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
|
handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
|
||||||
wristAbsRotation = wrists[h].controller.getAbsRotation();
|
wristAbsRotation = wrists[h].controller.getAbsRotation();
|
||||||
handPitch = 2.0 * -wristAbsRotation.x;
|
handPitch = 2.0 * -wristAbsRotation.x;
|
||||||
|
@ -314,12 +378,12 @@ var leapHands = (function () {
|
||||||
if (h === 0) {
|
if (h === 0) {
|
||||||
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
|
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
|
||||||
Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
|
Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
|
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
|
||||||
Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
|
Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
|
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
|
||||||
|
|
||||||
// Finger joints ...
|
// Finger joints ...
|
||||||
|
|
|
@ -137,8 +137,9 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||||
set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
|
set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE")
|
||||||
target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
|
target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_lastNackTime(usecTimestampNow()),
|
_lastNackTime(usecTimestampNow()),
|
||||||
_lastSendDownstreamAudioStats(usecTimestampNow())
|
_lastSendDownstreamAudioStats(usecTimestampNow())
|
||||||
{
|
{
|
||||||
|
|
||||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||||
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||||
|
|
||||||
|
@ -304,9 +303,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequired,
|
connect(&addressManager, &AddressManager::possibleDomainChangeRequired,
|
||||||
this, &Application::changeDomainHostname);
|
this, &Application::changeDomainHostname);
|
||||||
|
|
||||||
// when -url in command line, teleport to location
|
|
||||||
addressManager.handleLookupString(getCmdOption(argc, constArgv, "-url"));
|
|
||||||
|
|
||||||
_settings = new QSettings(this);
|
_settings = new QSettings(this);
|
||||||
_numChangedSettings = 0;
|
_numChangedSettings = 0;
|
||||||
|
|
||||||
|
@ -381,12 +377,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_particleEditSender.setPacketsPerSecond(3000); // super high!!
|
_particleEditSender.setPacketsPerSecond(3000); // super high!!
|
||||||
_entityEditSender.setPacketsPerSecond(3000); // super high!!
|
_entityEditSender.setPacketsPerSecond(3000); // super high!!
|
||||||
|
|
||||||
// Set the sixense filtering
|
|
||||||
_sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
|
|
||||||
|
|
||||||
// Set hand controller velocity filtering
|
|
||||||
_sixenseManager.setLowVelocityFilter(Menu::getInstance()->isOptionChecked(MenuOption::LowVelocityFilter));
|
|
||||||
|
|
||||||
checkVersion();
|
checkVersion();
|
||||||
|
|
||||||
_overlays.init(_glWidget); // do this before scripts load
|
_overlays.init(_glWidget); // do this before scripts load
|
||||||
|
@ -609,8 +599,11 @@ void Application::paintGL() {
|
||||||
|
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||||
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
|
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
|
||||||
|
if (!OculusManager::isConnected()) {
|
||||||
_myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition());
|
_myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition());
|
||||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
|
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
|
||||||
|
}
|
||||||
|
// OculusManager::display() updates camera position and rotation a bit further on.
|
||||||
|
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
} 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.
|
//Note, the camera distance is set in Camera::setMode() so we dont have to do it here.
|
||||||
|
@ -640,7 +633,9 @@ void Application::paintGL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update camera position
|
// 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
|
// 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
|
// main camera, vs, an alternate camera. The alternate camera we support right now
|
||||||
|
@ -650,7 +645,7 @@ void Application::paintGL() {
|
||||||
// Why have two cameras? Well, one reason is that because in the case of the renderViewFrustum()
|
// 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
|
// 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
|
// 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)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) {
|
||||||
|
|
||||||
|
@ -664,7 +659,7 @@ void Application::paintGL() {
|
||||||
_viewFrustumOffsetCamera.setDistance(viewFrustumOffset.distance);
|
_viewFrustumOffsetCamera.setDistance(viewFrustumOffset.distance);
|
||||||
_viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation
|
_viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation
|
||||||
_viewFrustumOffsetCamera.update(1.f/_fps);
|
_viewFrustumOffsetCamera.update(1.f/_fps);
|
||||||
whichCamera = _viewFrustumOffsetCamera;
|
whichCamera = &_viewFrustumOffsetCamera;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Menu::getInstance()->getShadowsEnabled()) {
|
if (Menu::getInstance()->getShadowsEnabled()) {
|
||||||
|
@ -677,15 +672,16 @@ void Application::paintGL() {
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
//When in mirror mode, use camera rotation. Otherwise, use body rotation
|
//When in mirror mode, use camera rotation. Otherwise, use body rotation
|
||||||
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) {
|
if (whichCamera->getMode() == CAMERA_MODE_MIRROR) {
|
||||||
OculusManager::display(whichCamera.getRotation(), whichCamera.getPosition(), whichCamera);
|
OculusManager::display(whichCamera->getRotation(), whichCamera->getPosition(), *whichCamera);
|
||||||
} else {
|
} else {
|
||||||
OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), whichCamera);
|
OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), *whichCamera);
|
||||||
}
|
}
|
||||||
|
_myCamera.update(1.f / _fps);
|
||||||
|
|
||||||
} else if (TV3DManager::isConnected()) {
|
} else if (TV3DManager::isConnected()) {
|
||||||
|
|
||||||
TV3DManager::display(whichCamera);
|
TV3DManager::display(*whichCamera);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_glowEffect.prepare();
|
_glowEffect.prepare();
|
||||||
|
@ -693,7 +689,7 @@ void Application::paintGL() {
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
displaySide(whichCamera);
|
displaySide(*whichCamera);
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||||
|
@ -1484,7 +1480,7 @@ void Application::setRenderVoxels(bool voxelRender) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
||||||
getSixenseManager()->setLowVelocityFilter(lowVelocityFilter);
|
SixenseManager::getInstance().setLowVelocityFilter(lowVelocityFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::doKillLocalVoxels() {
|
void Application::doKillLocalVoxels() {
|
||||||
|
@ -1797,7 +1793,25 @@ void Application::init() {
|
||||||
Menu::getInstance()->loadSettings();
|
Menu::getInstance()->loadSettings();
|
||||||
_audio.setReceivedAudioStreamSettings(Menu::getInstance()->getReceivedAudioStreamSettings());
|
_audio.setReceivedAudioStreamSettings(Menu::getInstance()->getReceivedAudioStreamSettings());
|
||||||
|
|
||||||
qDebug("Loaded settings");
|
qDebug() << "Loaded settings";
|
||||||
|
|
||||||
|
// when --url in command line, teleport to location
|
||||||
|
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||||
|
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||||
|
if (urlIndex != -1) {
|
||||||
|
AddressManager::getInstance().handleLookupString(arguments().value(urlIndex + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseEnabled)) {
|
||||||
|
// on OS X we only setup sixense if the user wants it on - this allows running without the hid_init crash
|
||||||
|
// if hydra support is temporarily not required
|
||||||
|
Menu::getInstance()->toggleSixense(true);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// setup sixense
|
||||||
|
Menu::getInstance()->toggleSixense(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
// initialize our face trackers after loading the menu settings
|
// initialize our face trackers after loading the menu settings
|
||||||
_faceshift.init();
|
_faceshift.init();
|
||||||
|
@ -2174,7 +2188,7 @@ void Application::update(float deltaTime) {
|
||||||
DeviceTracker::updateAll();
|
DeviceTracker::updateAll();
|
||||||
updateFaceshift();
|
updateFaceshift();
|
||||||
updateVisage();
|
updateVisage();
|
||||||
_sixenseManager.update(deltaTime);
|
SixenseManager::getInstance().update(deltaTime);
|
||||||
JoystickScriptingInterface::getInstance().update();
|
JoystickScriptingInterface::getInstance().update();
|
||||||
_prioVR.update(deltaTime);
|
_prioVR.update(deltaTime);
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,6 @@ public:
|
||||||
DdeFaceTracker* getDDE() { return &_dde; }
|
DdeFaceTracker* getDDE() { return &_dde; }
|
||||||
CaraFaceTracker* getCara() { return &_cara; }
|
CaraFaceTracker* getCara() { return &_cara; }
|
||||||
FaceTracker* getActiveFaceTracker();
|
FaceTracker* getActiveFaceTracker();
|
||||||
SixenseManager* getSixenseManager() { return &_sixenseManager; }
|
|
||||||
PrioVR* getPrioVR() { return &_prioVR; }
|
PrioVR* getPrioVR() { return &_prioVR; }
|
||||||
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
||||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||||
|
@ -510,7 +509,6 @@ private:
|
||||||
CaraFaceTracker _cara;
|
CaraFaceTracker _cara;
|
||||||
DdeFaceTracker _dde;
|
DdeFaceTracker _dde;
|
||||||
|
|
||||||
SixenseManager _sixenseManager;
|
|
||||||
PrioVR _prioVR;
|
PrioVR _prioVR;
|
||||||
|
|
||||||
Camera _myCamera; // My view onto the world
|
Camera _myCamera; // My view onto the world
|
||||||
|
|
|
@ -431,11 +431,18 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||||
|
|
||||||
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||||
|
#ifdef __APPLE__
|
||||||
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||||
|
MenuOption::SixenseEnabled,
|
||||||
|
0, false,
|
||||||
|
this,
|
||||||
|
SLOT(toggleSixense(bool)));
|
||||||
|
#endif
|
||||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||||
MenuOption::FilterSixense,
|
MenuOption::FilterSixense,
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
appInstance->getSixenseManager(),
|
&SixenseManager::getInstance(),
|
||||||
SLOT(setFilter(bool)));
|
SLOT(setFilter(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||||
MenuOption::LowVelocityFilter,
|
MenuOption::LowVelocityFilter,
|
||||||
|
@ -446,6 +453,9 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false);
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false);
|
||||||
|
|
||||||
|
QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||||
|
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||||
|
|
||||||
QMenu* networkMenu = developerMenu->addMenu("Network");
|
QMenu* networkMenu = developerMenu->addMenu("Network");
|
||||||
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
|
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||||
|
@ -1134,6 +1144,18 @@ void Menu::editAnimations() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::toggleSixense(bool shouldEnable) {
|
||||||
|
SixenseManager& sixenseManager = SixenseManager::getInstance();
|
||||||
|
|
||||||
|
if (shouldEnable && !sixenseManager.isInitialized()) {
|
||||||
|
sixenseManager.initialize();
|
||||||
|
sixenseManager.setFilter(isOptionChecked(MenuOption::FilterSixense));
|
||||||
|
sixenseManager.setLowVelocityFilter(isOptionChecked(MenuOption::LowVelocityFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
sixenseManager.setIsEnabled(shouldEnable);
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::changePrivateKey() {
|
void Menu::changePrivateKey() {
|
||||||
// setup the dialog
|
// setup the dialog
|
||||||
QInputDialog privateKeyDialog(Application::getInstance()->getWindow());
|
QInputDialog privateKeyDialog(Application::getInstance()->getWindow());
|
||||||
|
|
|
@ -186,6 +186,7 @@ public slots:
|
||||||
void pasteToVoxel();
|
void pasteToVoxel();
|
||||||
|
|
||||||
void toggleLoginMenuItem();
|
void toggleLoginMenuItem();
|
||||||
|
void toggleSixense(bool shouldEnable);
|
||||||
|
|
||||||
QMenu* addMenu(const QString& menuName);
|
QMenu* addMenu(const QString& menuName);
|
||||||
void removeMenu(const QString& menuName);
|
void removeMenu(const QString& menuName);
|
||||||
|
@ -402,6 +403,7 @@ namespace MenuOption {
|
||||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||||
const QString IncreaseVoxelSize = "Increase Voxel Size";
|
const QString IncreaseVoxelSize = "Increase Voxel Size";
|
||||||
const QString KeyboardMotorControl = "Enable Keyboard Motor Control";
|
const QString KeyboardMotorControl = "Enable Keyboard Motor Control";
|
||||||
|
const QString LeapMotionOnHMD = "Leap Motion on HMD";
|
||||||
const QString LoadScript = "Open and Run Script File...";
|
const QString LoadScript = "Open and Run Script File...";
|
||||||
const QString LoadScriptURL = "Open and Run Script from URL...";
|
const QString LoadScriptURL = "Open and Run Script from URL...";
|
||||||
const QString LodTools = "LOD Tools";
|
const QString LodTools = "LOD Tools";
|
||||||
|
@ -448,6 +450,7 @@ namespace MenuOption {
|
||||||
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
||||||
const QString ShowIKConstraints = "Show IK Constraints";
|
const QString ShowIKConstraints = "Show IK Constraints";
|
||||||
const QString SimpleShadows = "Simple";
|
const QString SimpleShadows = "Simple";
|
||||||
|
const QString SixenseEnabled = "Enable Hydra Support";
|
||||||
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||||
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
||||||
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
||||||
|
|
|
@ -1204,7 +1204,8 @@ int PointAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||||
_points.swap(swapPoints);
|
_points.swap(swapPoints);
|
||||||
buffer = new PointBuffer(swapPoints);
|
buffer = new PointBuffer(swapPoints);
|
||||||
}
|
}
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
BufferDataPointer pointer(buffer);
|
||||||
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||||
}
|
}
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
@ -1219,7 +1220,8 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) {
|
||||||
_points.swap(swapPoints);
|
_points.swap(swapPoints);
|
||||||
buffer = new PointBuffer(swapPoints);
|
buffer = new PointBuffer(swapPoints);
|
||||||
}
|
}
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
BufferDataPointer pointer(buffer);
|
||||||
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1446,7 +1448,8 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
|
||||||
_data->guide(_fetchVisitor);
|
_data->guide(_fetchVisitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
BufferDataPointer pointer(buffer);
|
||||||
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1505,7 +1508,8 @@ int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) {
|
||||||
buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials());
|
buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials());
|
||||||
_fetchVisitor.init(newBuffer);
|
_fetchVisitor.init(newBuffer);
|
||||||
_data->guide(_fetchVisitor);
|
_data->guide(_fetchVisitor);
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer)));
|
BufferDataPointer pointer(newBuffer);
|
||||||
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1878,114 +1882,42 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||||
glm::mat3 atrans = glm::transpose(a);
|
glm::mat3 atrans = glm::transpose(a);
|
||||||
glm::mat3 ata = atrans * a;
|
glm::mat3 ata = atrans * a;
|
||||||
|
|
||||||
// compute the SVD of ata; first, find the eigenvalues
|
// find the eigenvalues and eigenvectors of ata
|
||||||
// (see http://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices)
|
// (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm)
|
||||||
glm::vec3 eigenvalues;
|
glm::mat3 d = ata;
|
||||||
float p1 = ata[0][1] * ata[0][1] + ata[0][2] * ata[0][2] + ata[1][2] * ata[1][2];
|
glm::quat combinedRotation;
|
||||||
if (p1 < EPSILON) {
|
const int MAX_ITERATIONS = 20;
|
||||||
eigenvalues = glm::vec3(ata[0][0], ata[1][1], ata[2][2]);
|
for (int i = 0; i < MAX_ITERATIONS; i++) {
|
||||||
if (eigenvalues[2] < eigenvalues[1]) {
|
glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1]));
|
||||||
qSwap(eigenvalues[2], eigenvalues[1]);
|
int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? (offDiagonals[0] > offDiagonals[2] ? 0 : 2) :
|
||||||
|
(offDiagonals[1] > offDiagonals[2] ? 1 : 2);
|
||||||
|
const float DESIRED_PRECISION = 0.00001f;
|
||||||
|
if (offDiagonals[largestIndex] < DESIRED_PRECISION) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (eigenvalues[1] < eigenvalues[0]) {
|
int largestJ = (largestIndex == 2) ? 1 : 0;
|
||||||
qSwap(eigenvalues[1], eigenvalues[0]);
|
int largestI = (largestIndex == 0) ? 1 : 2;
|
||||||
|
float sjj = d[largestJ][largestJ];
|
||||||
|
float sii = d[largestI][largestI];
|
||||||
|
float angle = (sii == sjj ? PI_OVER_TWO : glm::atan(2.0f * d[largestJ][largestI], sjj - sii)) / 2.0f;
|
||||||
|
glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) :
|
||||||
|
(largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f)));
|
||||||
|
if (rotation.w == 0.0f) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (eigenvalues[2] < eigenvalues[1]) {
|
combinedRotation = glm::normalize(rotation * combinedRotation);
|
||||||
qSwap(eigenvalues[2], eigenvalues[1]);
|
glm::mat3 matrix = glm::mat3_cast(combinedRotation);
|
||||||
}
|
d = matrix * ata * glm::transpose(matrix);
|
||||||
} else {
|
|
||||||
float q = (ata[0][0] + ata[1][1] + ata[2][2]) / 3.0f;
|
|
||||||
float d1 = ata[0][0] - q, d2 = ata[1][1] - q, d3 = ata[2][2] - q;
|
|
||||||
float p2 = d1 * d1 + d2 * d2 + d3 * d3 + 2.0f * p1;
|
|
||||||
float p = glm::sqrt(p2 / 6.0f);
|
|
||||||
glm::mat3 b = (ata - glm::mat3(q)) / p;
|
|
||||||
float r = glm::determinant(b) / 2.0f;
|
|
||||||
float phi;
|
|
||||||
if (r <= -1.0f) {
|
|
||||||
phi = PI / 3.0f;
|
|
||||||
} else if (r >= 1.0f) {
|
|
||||||
phi = 0.0f;
|
|
||||||
} else {
|
|
||||||
phi = glm::acos(r) / 3.0f;
|
|
||||||
}
|
|
||||||
eigenvalues[2] = q + 2.0f * p * glm::cos(phi);
|
|
||||||
eigenvalues[0] = q + 2.0f * p * glm::cos(phi + (2.0f * PI / 3.0f));
|
|
||||||
eigenvalues[1] = 3.0f * q - eigenvalues[0] - eigenvalues[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// form the singular matrix from the eigenvalues
|
// form the singular matrix from the eigenvalues
|
||||||
glm::mat3 d;
|
|
||||||
const float MIN_SINGULAR_THRESHOLD = 0.1f;
|
const float MIN_SINGULAR_THRESHOLD = 0.1f;
|
||||||
d[0][0] = (eigenvalues[0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / eigenvalues[0];
|
d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0];
|
||||||
d[1][1] = (eigenvalues[1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / eigenvalues[1];
|
d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1];
|
||||||
d[2][2] = (eigenvalues[2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / eigenvalues[2];
|
d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2];
|
||||||
|
|
||||||
glm::mat3 m[] = { ata - glm::mat3(eigenvalues[0]), ata - glm::mat3(eigenvalues[1]),
|
|
||||||
ata - glm::mat3(eigenvalues[2]) };
|
|
||||||
|
|
||||||
// form the orthogonal matrix from the eigenvectors
|
|
||||||
// see http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf
|
|
||||||
bool same01 = glm::abs(eigenvalues[0] - eigenvalues[1]) < EPSILON;
|
|
||||||
bool same12 = glm::abs(eigenvalues[1] - eigenvalues[2]) < EPSILON;
|
|
||||||
glm::mat3 u;
|
|
||||||
if (!(same01 && same12)) {
|
|
||||||
if (same01 || same12) {
|
|
||||||
int i = same01 ? 2 : 0;
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
glm::vec3 first = glm::vec3(m[i][0][j], m[i][1][j], m[i][2][j]);
|
|
||||||
int j2 = (j + 1) % 3;
|
|
||||||
glm::vec3 second = glm::vec3(m[i][0][j2], m[i][1][j2], m[i][2][j2]);
|
|
||||||
glm::vec3 cross = glm::cross(first, second);
|
|
||||||
float length = glm::length(cross);
|
|
||||||
if (length > EPSILON) {
|
|
||||||
u[0][i] = cross[0] / length;
|
|
||||||
u[1][i] = cross[1] / length;
|
|
||||||
u[2][i] = cross[2] / length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i = (i + 1) % 3;
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
glm::vec3 first = glm::vec3(m[i][0][j], m[i][1][j], m[i][2][j]);
|
|
||||||
float length = glm::length(first);
|
|
||||||
if (length > EPSILON) {
|
|
||||||
glm::vec3 second = glm::cross(first, glm::vec3(1.0f, 0.0f, 0.0f));
|
|
||||||
length = glm::length(second);
|
|
||||||
if (length < EPSILON) {
|
|
||||||
second = glm::cross(first, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
||||||
length = glm::length(second);
|
|
||||||
}
|
|
||||||
u[0][i] = second[0] / length;
|
|
||||||
u[1][i] = second[1] / length;
|
|
||||||
u[2][i] = second[2] / length;
|
|
||||||
second = glm::normalize(glm::cross(second, first));
|
|
||||||
i = (i + 1) % 3;
|
|
||||||
u[0][i] = second[0];
|
|
||||||
u[1][i] = second[1];
|
|
||||||
u[2][i] = second[2];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
glm::vec3 first = glm::vec3(m[i][0][j], m[i][1][j], m[i][2][j]);
|
|
||||||
int j2 = (j + 1) % 3;
|
|
||||||
glm::vec3 second = glm::vec3(m[i][0][j2], m[i][1][j2], m[i][2][j2]);
|
|
||||||
glm::vec3 cross = glm::cross(first, second);
|
|
||||||
float length = glm::length(cross);
|
|
||||||
if (length > EPSILON) {
|
|
||||||
u[0][i] = cross[0] / length;
|
|
||||||
u[1][i] = cross[1] / length;
|
|
||||||
u[2][i] = cross[2] / length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute the pseudo-inverse, ataplus, and use to find the minimizing solution
|
// compute the pseudo-inverse, ataplus, and use to find the minimizing solution
|
||||||
|
glm::mat3 u = glm::mat3_cast(combinedRotation);
|
||||||
glm::mat3 ataplus = glm::transpose(u) * d * u;
|
glm::mat3 ataplus = glm::transpose(u) * d * u;
|
||||||
glm::vec3 solution = (ataplus * atrans * b) + center;
|
glm::vec3 solution = (ataplus * atrans * b) + center;
|
||||||
|
|
||||||
|
@ -2078,7 +2010,8 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||||
|
|
||||||
buffer = new VoxelBuffer(vertices, indices, material->getMaterials());
|
buffer = new VoxelBuffer(vertices, indices, material->getMaterials());
|
||||||
}
|
}
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
BufferDataPointer pointer(buffer);
|
||||||
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -520,10 +520,9 @@ void Avatar::renderBody(RenderMode renderMode, bool postLighting, float glowLeve
|
||||||
renderAttachments(renderMode);
|
renderAttachments(renderMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!postLighting) {
|
getHead()->render(1.0f, modelRenderMode, postLighting);
|
||||||
getHead()->render(1.0f, modelRenderMode);
|
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
if (!postLighting && Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
||||||
// Render Hair
|
// Render Hair
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glm::vec3 headPosition = getHead()->getPosition();
|
glm::vec3 headPosition = getHead()->getPosition();
|
||||||
|
@ -534,7 +533,6 @@ void Avatar::renderBody(RenderMode renderMode, bool postLighting, float glowLeve
|
||||||
_hair.render();
|
_hair.render();
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const {
|
bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const {
|
||||||
|
|
|
@ -211,15 +211,14 @@ void Head::relaxLean(float deltaTime) {
|
||||||
_deltaLeanForward *= relaxationFactor;
|
_deltaLeanForward *= relaxationFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::render(float alpha, Model::RenderMode mode) {
|
void Head::render(float alpha, Model::RenderMode mode, bool postLighting) {
|
||||||
_faceModel.render(alpha, mode);
|
if (postLighting) {
|
||||||
if (_renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
|
if (_renderLookatVectors) {
|
||||||
Application::getInstance()->getDeferredLightingEffect()->addPostLightingRenderable(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Head::renderPostLighting() {
|
|
||||||
renderLookatVectors(_leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
|
renderLookatVectors(_leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_faceModel.render(alpha, mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::setScale (float scale) {
|
void Head::setScale (float scale) {
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "FaceModel.h"
|
#include "FaceModel.h"
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "renderer/DeferredLightingEffect.h"
|
|
||||||
|
|
||||||
enum eyeContactTargets {
|
enum eyeContactTargets {
|
||||||
LEFT_EYE,
|
LEFT_EYE,
|
||||||
|
@ -36,15 +35,14 @@ const float EYE_EAR_GAP = 0.08f;
|
||||||
class Avatar;
|
class Avatar;
|
||||||
class ProgramObject;
|
class ProgramObject;
|
||||||
|
|
||||||
class Head : public HeadData, public PostLightingRenderable {
|
class Head : public HeadData {
|
||||||
public:
|
public:
|
||||||
Head(Avatar* owningAvatar);
|
Head(Avatar* owningAvatar);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void reset();
|
void reset();
|
||||||
void simulate(float deltaTime, bool isMine, bool billboard = false);
|
void simulate(float deltaTime, bool isMine, bool billboard = false);
|
||||||
void render(float alpha, Model::RenderMode mode);
|
void render(float alpha, Model::RenderMode mode, bool postLighting);
|
||||||
virtual void renderPostLighting();
|
|
||||||
void setScale(float scale);
|
void setScale(float scale);
|
||||||
void setPosition(glm::vec3 position) { _position = position; }
|
void setPosition(glm::vec3 position) { _position = position; }
|
||||||
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
||||||
|
|
|
@ -1147,10 +1147,9 @@ void MyAvatar::renderBody(RenderMode renderMode, bool postLighting, float glowLe
|
||||||
const Camera *camera = Application::getInstance()->getCamera();
|
const Camera *camera = Application::getInstance()->getCamera();
|
||||||
const glm::vec3 cameraPos = camera->getPosition() + (camera->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) * camera->getDistance();
|
const glm::vec3 cameraPos = camera->getPosition() + (camera->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) * camera->getDistance();
|
||||||
if (shouldRenderHead(cameraPos, renderMode)) {
|
if (shouldRenderHead(cameraPos, renderMode)) {
|
||||||
if (!postLighting) {
|
getHead()->render(1.0f, modelRenderMode, postLighting);
|
||||||
getHead()->render(1.0f, modelRenderMode);
|
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
if (!postLighting && Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
||||||
// Render Hair
|
// Render Hair
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glm::vec3 headPosition = getHead()->getPosition();
|
glm::vec3 headPosition = getHead()->getPosition();
|
||||||
|
@ -1162,7 +1161,6 @@ void MyAvatar::renderBody(RenderMode renderMode, bool postLighting, float glowLe
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (postLighting) {
|
if (postLighting) {
|
||||||
getHand()->render(true, modelRenderMode);
|
getHand()->render(true, modelRenderMode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,9 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "SharedUtil.h"
|
|
||||||
#include "Leapmotion.h"
|
#include "Leapmotion.h"
|
||||||
|
#include "Menu.h"
|
||||||
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
const int PALMROOT_NUM_JOINTS = 3;
|
const int PALMROOT_NUM_JOINTS = 3;
|
||||||
const int FINGER_NUM_JOINTS = 4;
|
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() {
|
Leapmotion::~Leapmotion() {
|
||||||
|
|
|
@ -412,6 +412,10 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
// Update camera for use by rest of Interface.
|
||||||
|
whichCamera.setTargetPosition((_leftEyePosition + _rightEyePosition) / 2.f);
|
||||||
|
whichCamera.setTargetRotation(_camera->getTargetRotation());
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,23 +30,28 @@ const int CALIBRATION_STATE_COMPLETE = 4;
|
||||||
const float NECK_X = 0.25f; // meters
|
const float NECK_X = 0.25f; // meters
|
||||||
const float NECK_Y = 0.3f; // meters
|
const float NECK_Y = 0.3f; // meters
|
||||||
const float NECK_Z = 0.3f; // meters
|
const float NECK_Z = 0.3f; // meters
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
typedef int (*SixenseBaseFunction)();
|
||||||
|
typedef int (*SixenseTakeIntFunction)(int);
|
||||||
|
typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SixenseManager::SixenseManager() {
|
|
||||||
#ifdef HAVE_SIXENSE
|
|
||||||
_lastMovement = 0;
|
|
||||||
_amountMoved = glm::vec3(0.0f);
|
|
||||||
_lowVelocityFilter = false;
|
|
||||||
|
|
||||||
_calibrationState = CALIBRATION_STATE_IDLE;
|
|
||||||
// By default we assume the _neckBase (in orb frame) is as high above the orb
|
|
||||||
// as the "torso" is below it.
|
|
||||||
_neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
|
|
||||||
|
|
||||||
sixenseInit();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
_hydrasConnected = false;
|
|
||||||
|
SixenseManager& SixenseManager::getInstance() {
|
||||||
|
static SixenseManager sharedInstance;
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SixenseManager::SixenseManager() :
|
||||||
|
#if defined(HAVE_SIXENSE) && defined(__APPLE__)
|
||||||
|
_sixenseLibrary(NULL),
|
||||||
|
#endif
|
||||||
|
_isInitialized(false),
|
||||||
|
_isEnabled(true),
|
||||||
|
_hydrasConnected(false)
|
||||||
|
{
|
||||||
_triggerPressed[0] = false;
|
_triggerPressed[0] = false;
|
||||||
_bumperPressed[0] = false;
|
_bumperPressed[0] = false;
|
||||||
_oldX[0] = -1;
|
_oldX[0] = -1;
|
||||||
|
@ -58,23 +63,77 @@ SixenseManager::SixenseManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SixenseManager::~SixenseManager() {
|
SixenseManager::~SixenseManager() {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE_
|
||||||
|
|
||||||
|
if (_isInitialized) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseBaseFunction sixenseExit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseExit");
|
||||||
|
#endif
|
||||||
|
|
||||||
sixenseExit();
|
sixenseExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
delete _sixenseLibrary;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SixenseManager::initialize() {
|
||||||
|
#ifdef HAVE_SIXENSE
|
||||||
|
|
||||||
|
if (!_isInitialized) {
|
||||||
|
_lastMovement = 0;
|
||||||
|
_amountMoved = glm::vec3(0.0f);
|
||||||
|
_lowVelocityFilter = false;
|
||||||
|
|
||||||
|
_calibrationState = CALIBRATION_STATE_IDLE;
|
||||||
|
// By default we assume the _neckBase (in orb frame) is as high above the orb
|
||||||
|
// as the "torso" is below it.
|
||||||
|
_neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
if (!_sixenseLibrary) {
|
||||||
|
const QString SIXENSE_LIBRARY_NAME = "libsixense_x64.dylib";
|
||||||
|
_sixenseLibrary = new QLibrary(SIXENSE_LIBRARY_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Initializing sixense library for hydra support - libsixense_x64.dylib load state is"
|
||||||
|
<< _sixenseLibrary->isLoaded();
|
||||||
|
SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sixenseInit();
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SixenseManager::setFilter(bool filter) {
|
void SixenseManager::setFilter(bool filter) {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
|
|
||||||
|
if (_isInitialized) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
sixenseSetFilterEnabled(1);
|
sixenseSetFilterEnabled(1);
|
||||||
} else {
|
} else {
|
||||||
sixenseSetFilterEnabled(0);
|
sixenseSetFilterEnabled(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SixenseManager::update(float deltaTime) {
|
void SixenseManager::update(float deltaTime) {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
|
if (_isInitialized && _isEnabled) {
|
||||||
// if the controllers haven't been moved in a while, disable
|
// if the controllers haven't been moved in a while, disable
|
||||||
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
||||||
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
||||||
|
@ -85,6 +144,11 @@ void SixenseManager::update(float deltaTime) {
|
||||||
_lastMovement = usecTimestampNow();
|
_lastMovement = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseBaseFunction sixenseGetNumActiveControllers =
|
||||||
|
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (sixenseGetNumActiveControllers() == 0) {
|
if (sixenseGetNumActiveControllers() == 0) {
|
||||||
_hydrasConnected = false;
|
_hydrasConnected = false;
|
||||||
return;
|
return;
|
||||||
|
@ -98,11 +162,24 @@ void SixenseManager::update(float deltaTime) {
|
||||||
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
||||||
Hand* hand = avatar->getHand();
|
Hand* hand = avatar->getHand();
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseBaseFunction sixenseGetMaxControllers =
|
||||||
|
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
|
||||||
|
#endif
|
||||||
|
|
||||||
int maxControllers = sixenseGetMaxControllers();
|
int maxControllers = sixenseGetMaxControllers();
|
||||||
|
|
||||||
// we only support two controllers
|
// we only support two controllers
|
||||||
sixenseControllerData controllers[2];
|
sixenseControllerData controllers[2];
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseTakeIntFunction sixenseIsControllerEnabled =
|
||||||
|
(SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled");
|
||||||
|
|
||||||
|
SixenseTakeIntAndSixenseControllerData sixenseGetNewestData =
|
||||||
|
(SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData");
|
||||||
|
#endif
|
||||||
|
|
||||||
int numActiveControllers = 0;
|
int numActiveControllers = 0;
|
||||||
for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) {
|
for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) {
|
||||||
if (!sixenseIsControllerEnabled(i)) {
|
if (!sixenseIsControllerEnabled(i)) {
|
||||||
|
@ -208,6 +285,8 @@ void SixenseManager::update(float deltaTime) {
|
||||||
if (numActiveControllers == 2) {
|
if (numActiveControllers == 2) {
|
||||||
updateCalibration(controllers);
|
updateCalibration(controllers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#endif // HAVE_SIXENSE
|
#endif // HAVE_SIXENSE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,11 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include "sixense.h"
|
#include "sixense.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <qlibrary.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
||||||
|
@ -38,9 +43,12 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
|
||||||
class SixenseManager : public QObject {
|
class SixenseManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
static SixenseManager& getInstance();
|
||||||
|
|
||||||
SixenseManager();
|
void initialize();
|
||||||
~SixenseManager();
|
bool isInitialized() const { return _isInitialized; }
|
||||||
|
|
||||||
|
void setIsEnabled(bool isEnabled) { _isEnabled = isEnabled; }
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
float getCursorPixelRangeMult() const;
|
float getCursorPixelRangeMult() const;
|
||||||
|
@ -51,6 +59,9 @@ public slots:
|
||||||
void setLowVelocityFilter(bool lowVelocityFilter) { _lowVelocityFilter = lowVelocityFilter; };
|
void setLowVelocityFilter(bool lowVelocityFilter) { _lowVelocityFilter = lowVelocityFilter; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SixenseManager();
|
||||||
|
~SixenseManager();
|
||||||
|
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
void updateCalibration(const sixenseControllerData* controllers);
|
void updateCalibration(const sixenseControllerData* controllers);
|
||||||
void emulateMouse(PalmData* palm, int index);
|
void emulateMouse(PalmData* palm, int index);
|
||||||
|
@ -72,7 +83,13 @@ private:
|
||||||
glm::vec3 _reachForward;
|
glm::vec3 _reachForward;
|
||||||
float _lastDistance;
|
float _lastDistance;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
QLibrary* _sixenseLibrary;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
bool _isInitialized;
|
||||||
|
bool _isEnabled;
|
||||||
bool _hydrasConnected;
|
bool _hydrasConnected;
|
||||||
quint64 _lastMovement;
|
quint64 _lastMovement;
|
||||||
glm::vec3 _amountMoved;
|
glm::vec3 _amountMoved;
|
||||||
|
|
|
@ -1335,11 +1335,7 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold
|
||||||
} else {
|
} else {
|
||||||
glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity);
|
glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity);
|
||||||
if (!(translucent && alphaThreshold == 0.0f)) {
|
if (!(translucent && alphaThreshold == 0.0f)) {
|
||||||
float emissive = (part.emissiveColor.r + part.emissiveColor.g + part.emissiveColor.b) / 3.0f;
|
glAlphaFunc(GL_EQUAL, diffuse.a = Application::getInstance()->getGlowEffect()->getIntensity());
|
||||||
diffuse.a = qMax(Application::getInstance()->getGlowEffect()->getIntensity(), emissive);
|
|
||||||
glAlphaFunc(GL_EQUAL, diffuse.a);
|
|
||||||
diffuse = glm::vec4(qMax(diffuse.r, part.emissiveColor.r), qMax(diffuse.g, part.emissiveColor.g),
|
|
||||||
qMax(diffuse.b, part.emissiveColor.b), diffuse.a);
|
|
||||||
}
|
}
|
||||||
glm::vec4 specular = glm::vec4(part.specularColor, 1.0f);
|
glm::vec4 specular = glm::vec4(part.specularColor, 1.0f);
|
||||||
glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse);
|
glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse);
|
||||||
|
|
|
@ -265,7 +265,11 @@ QScriptValue WindowScriptingInterface::doPeekNonBlockingFormResult(QScriptValue
|
||||||
_form.setProperty(i, item);
|
_form.setProperty(i, item);
|
||||||
} else if (item.property("options").isArray()) {
|
} else if (item.property("options").isArray()) {
|
||||||
c += 1;
|
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);
|
_form.setProperty(i, item);
|
||||||
} else {
|
} else {
|
||||||
e += 1;
|
e += 1;
|
||||||
|
@ -318,7 +322,11 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
|
||||||
_form.setProperty(i, item);
|
_form.setProperty(i, item);
|
||||||
} else if (item.property("options").isArray()) {
|
} else if (item.property("options").isArray()) {
|
||||||
c += 1;
|
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);
|
_form.setProperty(i, item);
|
||||||
} else {
|
} else {
|
||||||
e += 1;
|
e += 1;
|
||||||
|
@ -349,6 +357,7 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
|
||||||
_form = QScriptValue();
|
_form = QScriptValue();
|
||||||
_edits.clear();
|
_edits.clear();
|
||||||
_directories.clear();
|
_directories.clear();
|
||||||
|
_combos.clear();
|
||||||
|
|
||||||
array = _form;
|
array = _form;
|
||||||
return (_formResult == QDialog::Accepted);
|
return (_formResult == QDialog::Accepted);
|
||||||
|
@ -391,8 +400,12 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
|
||||||
form.setProperty(i, item);
|
form.setProperty(i, item);
|
||||||
} else if (item.property("options").isArray()) {
|
} else if (item.property("options").isArray()) {
|
||||||
c += 1;
|
c += 1;
|
||||||
item.setProperty("value", _combos.at(c)->currentText());
|
item.setProperty("value",
|
||||||
_form.setProperty(i, item);
|
_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 {
|
} else {
|
||||||
e += 1;
|
e += 1;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
@ -418,6 +431,7 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
|
||||||
}
|
}
|
||||||
|
|
||||||
delete editDialog;
|
delete editDialog;
|
||||||
|
_combos.clear();
|
||||||
_edits.clear();
|
_edits.clear();
|
||||||
_directories.clear();
|
_directories.clear();
|
||||||
return (result == QDialog::Accepted);
|
return (result == QDialog::Accepted);
|
||||||
|
@ -498,9 +512,9 @@ QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue
|
||||||
} else if (item.property("options").isArray()) {
|
} else if (item.property("options").isArray()) {
|
||||||
QComboBox* combo = new QComboBox();
|
QComboBox* combo = new QComboBox();
|
||||||
combo->setMinimumWidth(200);
|
combo->setMinimumWidth(200);
|
||||||
QStringList options = item.property("options").toVariant().toStringList();
|
qint32 options_count = item.property("options").property("length").toInt32();
|
||||||
for (QStringList::const_iterator it = options.begin(); it != options.end(); it += 1) {
|
for (qint32 i = 0; i < options_count; i++) {
|
||||||
combo->addItem(*it);
|
combo->addItem(item.property("options").property(i).toString());
|
||||||
}
|
}
|
||||||
_combos.push_back(combo);
|
_combos.push_back(combo);
|
||||||
formLayout->addRow(new QLabel(item.property("label").toString()), combo);
|
formLayout->addRow(new QLabel(item.property("label").toString()), combo);
|
||||||
|
|
|
@ -671,7 +671,7 @@ void ApplicationOverlay::renderControllerPointers() {
|
||||||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||||
|
|
||||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||||
float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult();
|
float cursorRange = glWidget->width() * SixenseManager::getInstance().getCursorPixelRangeMult();
|
||||||
|
|
||||||
mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle);
|
mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle);
|
||||||
mouseY = (glWidget->height() / 2.0f + cursorRange * yAngle);
|
mouseY = (glWidget->height() / 2.0f + cursorRange * yAngle);
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#include "LoginDialog.h"
|
#include "LoginDialog.h"
|
||||||
|
|
||||||
const QString FORGOT_PASSWORD_URL = "https://data.highfidelity.io/password/new";
|
const QString FORGOT_PASSWORD_URL = "https://data.highfidelity.io/users/password/new";
|
||||||
|
|
||||||
LoginDialog::LoginDialog(QWidget* parent) :
|
LoginDialog::LoginDialog(QWidget* parent) :
|
||||||
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
|
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
|
||||||
|
|
|
@ -96,6 +96,7 @@ void AudioInjector::injectAudio() {
|
||||||
packetStream << radius;
|
packetStream << radius;
|
||||||
|
|
||||||
// pack 255 for attenuation byte
|
// pack 255 for attenuation byte
|
||||||
|
int volumeOptionOffset = injectAudioPacket.size();
|
||||||
quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume();
|
quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume();
|
||||||
packetStream << volume;
|
packetStream << volume;
|
||||||
|
|
||||||
|
@ -118,6 +119,8 @@ void AudioInjector::injectAudio() {
|
||||||
memcpy(injectAudioPacket.data() + orientationOptionOffset,
|
memcpy(injectAudioPacket.data() + orientationOptionOffset,
|
||||||
&_options.getOrientation(),
|
&_options.getOrientation(),
|
||||||
sizeof(_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
|
// resize the QByteArray to the right size
|
||||||
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
|
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
|
||||||
|
|
|
@ -652,13 +652,25 @@ void AvatarData::startPlaying() {
|
||||||
_player->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) {
|
if (_player) {
|
||||||
_player->setCurrentFrame(frame);
|
_player->setCurrentFrame(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::setPlayerTime(qint64 time) {
|
void AvatarData::setPlayerTime(unsigned int time) {
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->setCurrentTime(time);
|
_player->setCurrentTime(time);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,8 +100,6 @@ enum KeyState {
|
||||||
DELETE_KEY_DOWN
|
DELETE_KEY_DOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
const glm::vec3 vec3Zero(0.0f);
|
|
||||||
|
|
||||||
class QDataStream;
|
class QDataStream;
|
||||||
|
|
||||||
class AttachmentData;
|
class AttachmentData;
|
||||||
|
@ -304,8 +302,10 @@ public slots:
|
||||||
|
|
||||||
void loadRecording(QString filename);
|
void loadRecording(QString filename);
|
||||||
void startPlaying();
|
void startPlaying();
|
||||||
void setPlayerFrame(int frame);
|
void setPlayerVolume(float volume);
|
||||||
void setPlayerTime(qint64 time);
|
void setPlayerAudioOffset(int audioOffset);
|
||||||
|
void setPlayerFrame(unsigned int frame);
|
||||||
|
void setPlayerTime(unsigned int time);
|
||||||
void setPlayFromCurrentLocation(bool playFromCurrentLocation);
|
void setPlayFromCurrentLocation(bool playFromCurrentLocation);
|
||||||
void setPlayerLoop(bool loop);
|
void setPlayerLoop(bool loop);
|
||||||
void setPlayerUseDisplayName(bool useDisplayName);
|
void setPlayerUseDisplayName(bool useDisplayName);
|
||||||
|
|
|
@ -17,11 +17,16 @@
|
||||||
#include "AvatarData.h"
|
#include "AvatarData.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
|
|
||||||
|
static const int INVALID_FRAME = -1;
|
||||||
|
|
||||||
Player::Player(AvatarData* avatar) :
|
Player::Player(AvatarData* avatar) :
|
||||||
_recording(new Recording()),
|
|
||||||
_pausedFrame(-1),
|
|
||||||
_timerOffset(0),
|
|
||||||
_avatar(avatar),
|
_avatar(avatar),
|
||||||
|
_recording(new Recording()),
|
||||||
|
_currentFrame(INVALID_FRAME),
|
||||||
|
_frameInterpolationFactor(0.0f),
|
||||||
|
_pausedFrame(INVALID_FRAME),
|
||||||
|
_timerOffset(0),
|
||||||
|
_audioOffset(0),
|
||||||
_audioThread(NULL),
|
_audioThread(NULL),
|
||||||
_playFromCurrentPosition(true),
|
_playFromCurrentPosition(true),
|
||||||
_loop(false),
|
_loop(false),
|
||||||
|
@ -31,8 +36,6 @@ Player::Player(AvatarData* avatar) :
|
||||||
_useSkeletonURL(true)
|
_useSkeletonURL(true)
|
||||||
{
|
{
|
||||||
_timer.invalidate();
|
_timer.invalidate();
|
||||||
_options.setLoop(false);
|
|
||||||
_options.setVolume(1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::isPlaying() const {
|
bool Player::isPlaying() const {
|
||||||
|
@ -40,7 +43,7 @@ bool Player::isPlaying() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::isPaused() const {
|
bool Player::isPaused() const {
|
||||||
return (_pausedFrame != -1);
|
return (_pausedFrame != INVALID_FRAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 Player::elapsed() const {
|
qint64 Player::elapsed() const {
|
||||||
|
@ -120,7 +123,7 @@ void Player::startPlaying() {
|
||||||
_timer.start();
|
_timer.start();
|
||||||
|
|
||||||
setCurrentFrame(_pausedFrame);
|
setCurrentFrame(_pausedFrame);
|
||||||
_pausedFrame = -1;
|
_pausedFrame = INVALID_FRAME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +131,7 @@ void Player::stopPlaying() {
|
||||||
if (!isPlaying()) {
|
if (!isPlaying()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_pausedFrame = -1;
|
_pausedFrame = INVALID_FRAME;
|
||||||
_timer.invalidate();
|
_timer.invalidate();
|
||||||
cleanupAudioThread();
|
cleanupAudioThread();
|
||||||
_avatar->clearJointsData();
|
_avatar->clearJointsData();
|
||||||
|
@ -199,12 +202,12 @@ void Player::loadFromFile(const QString& file) {
|
||||||
}
|
}
|
||||||
readRecordingFromFile(_recording, file);
|
readRecordingFromFile(_recording, file);
|
||||||
|
|
||||||
_pausedFrame = -1;
|
_pausedFrame = INVALID_FRAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::loadRecording(RecordingPointer recording) {
|
void Player::loadRecording(RecordingPointer recording) {
|
||||||
_recording = recording;
|
_recording = recording;
|
||||||
_pausedFrame = -1;
|
_pausedFrame = INVALID_FRAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::play() {
|
void Player::play() {
|
||||||
|
@ -223,22 +226,68 @@ void Player::play() {
|
||||||
context = &_currentContext;
|
context = &_currentContext;
|
||||||
}
|
}
|
||||||
const RecordingFrame& currentFrame = _recording->getFrame(_currentFrame);
|
const RecordingFrame& currentFrame = _recording->getFrame(_currentFrame);
|
||||||
|
const RecordingFrame& nextFrame = _recording->getFrame(_currentFrame + 1);
|
||||||
|
|
||||||
_avatar->setPosition(context->position + context->orientation * currentFrame.getTranslation());
|
glm::vec3 translation = glm::mix(currentFrame.getTranslation(),
|
||||||
_avatar->setOrientation(context->orientation * currentFrame.getRotation());
|
nextFrame.getTranslation(),
|
||||||
_avatar->setTargetScale(context->scale * currentFrame.getScale());
|
_frameInterpolationFactor);
|
||||||
_avatar->setJointRotations(currentFrame.getJointRotations());
|
_avatar->setPosition(context->position + context->orientation * translation);
|
||||||
|
|
||||||
|
glm::quat rotation = safeMix(currentFrame.getRotation(),
|
||||||
|
nextFrame.getRotation(),
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
_avatar->setOrientation(context->orientation * rotation);
|
||||||
|
|
||||||
|
float scale = glm::mix(currentFrame.getScale(),
|
||||||
|
nextFrame.getScale(),
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
_avatar->setTargetScale(context->scale * scale);
|
||||||
|
|
||||||
|
|
||||||
|
QVector<glm::quat> jointRotations(currentFrame.getJointRotations().size());
|
||||||
|
for (int i = 0; i < currentFrame.getJointRotations().size(); ++i) {
|
||||||
|
jointRotations[i] = safeMix(currentFrame.getJointRotations()[i],
|
||||||
|
nextFrame.getJointRotations()[i],
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
}
|
||||||
|
_avatar->setJointRotations(jointRotations);
|
||||||
|
|
||||||
HeadData* head = const_cast<HeadData*>(_avatar->getHeadData());
|
HeadData* head = const_cast<HeadData*>(_avatar->getHeadData());
|
||||||
if (head) {
|
if (head) {
|
||||||
head->setBlendshapeCoefficients(currentFrame.getBlendshapeCoefficients());
|
// Make sure fake faceshift connection doesn't get turned off
|
||||||
head->setLeanSideways(currentFrame.getLeanSideways());
|
_avatar->setForceFaceshiftConnected(true);
|
||||||
head->setLeanForward(currentFrame.getLeanForward());
|
|
||||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(currentFrame.getHeadRotation()));
|
QVector<float> blendCoef(currentFrame.getBlendshapeCoefficients().size());
|
||||||
|
for (int i = 0; i < currentFrame.getBlendshapeCoefficients().size(); ++i) {
|
||||||
|
blendCoef[i] = glm::mix(currentFrame.getBlendshapeCoefficients()[i],
|
||||||
|
nextFrame.getBlendshapeCoefficients()[i],
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
}
|
||||||
|
head->setBlendshapeCoefficients(blendCoef);
|
||||||
|
|
||||||
|
float leanSideways = glm::mix(currentFrame.getLeanSideways(),
|
||||||
|
nextFrame.getLeanSideways(),
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
head->setLeanSideways(leanSideways);
|
||||||
|
|
||||||
|
float leanForward = glm::mix(currentFrame.getLeanForward(),
|
||||||
|
nextFrame.getLeanForward(),
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
head->setLeanForward(leanForward);
|
||||||
|
|
||||||
|
glm::quat headRotation = safeMix(currentFrame.getHeadRotation(),
|
||||||
|
nextFrame.getHeadRotation(),
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
glm::vec3 eulers = glm::degrees(safeEulerAngles(headRotation));
|
||||||
head->setFinalPitch(eulers.x);
|
head->setFinalPitch(eulers.x);
|
||||||
head->setFinalYaw(eulers.y);
|
head->setFinalYaw(eulers.y);
|
||||||
head->setFinalRoll(eulers.z);
|
head->setFinalRoll(eulers.z);
|
||||||
head->setLookAtPosition(context->position + context->orientation * currentFrame.getLookAtPosition());
|
|
||||||
|
|
||||||
|
glm::vec3 lookAt = glm::mix(currentFrame.getLookAtPosition(),
|
||||||
|
nextFrame.getLookAtPosition(),
|
||||||
|
_frameInterpolationFactor);
|
||||||
|
head->setLookAtPosition(context->position + context->orientation * lookAt);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "WARNING: Player couldn't find head data.";
|
qDebug() << "WARNING: Player couldn't find head data.";
|
||||||
}
|
}
|
||||||
|
@ -248,8 +297,8 @@ void Player::play() {
|
||||||
_injector->setOptions(_options);
|
_injector->setOptions(_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setCurrentFrame(int currentFrame) {
|
void Player::setCurrentFrame(unsigned int currentFrame) {
|
||||||
if (_recording && (currentFrame < 0 || currentFrame >= _recording->getFrameNumber())) {
|
if (_recording && currentFrame >= _recording->getFrameNumber()) {
|
||||||
stopPlaying();
|
stopPlaying();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -261,12 +310,12 @@ void Player::setCurrentFrame(int currentFrame) {
|
||||||
_timer.start();
|
_timer.start();
|
||||||
setAudionInjectorPosition();
|
setAudionInjectorPosition();
|
||||||
} else {
|
} else {
|
||||||
_pausedFrame = currentFrame;
|
_pausedFrame = _currentFrame;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setCurrentTime(qint64 currentTime) {
|
void Player::setCurrentTime(unsigned int currentTime) {
|
||||||
if (currentTime < 0 || currentTime >= _recording->getLength()) {
|
if (currentTime >= _recording->getLength()) {
|
||||||
stopPlaying();
|
stopPlaying();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -310,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() {
|
void Player::setAudionInjectorPosition() {
|
||||||
int MSEC_PER_SEC = 1000;
|
int MSEC_PER_SEC = 1000;
|
||||||
int SAMPLE_SIZE = 2; // 16 bits
|
int SAMPLE_SIZE = 2; // 16 bits
|
||||||
|
@ -325,17 +386,38 @@ void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) {
|
||||||
|
|
||||||
bool Player::computeCurrentFrame() {
|
bool Player::computeCurrentFrame() {
|
||||||
if (!isPlaying()) {
|
if (!isPlaying()) {
|
||||||
_currentFrame = -1;
|
_currentFrame = INVALID_FRAME;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (_currentFrame < 0) {
|
if (_currentFrame < 0) {
|
||||||
_currentFrame = 0;
|
_currentFrame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (_currentFrame < _recording->getFrameNumber() - 1 &&
|
quint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength());
|
||||||
_recording->getFrameTimestamp(_currentFrame) < elapsed()) {
|
while(_currentFrame >= 0 &&
|
||||||
++_currentFrame;
|
_recording->getFrameTimestamp(_currentFrame) > elapsed) {
|
||||||
|
--_currentFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (_currentFrame < _recording->getFrameNumber() &&
|
||||||
|
_recording->getFrameTimestamp(_currentFrame) < elapsed) {
|
||||||
|
++_currentFrame;
|
||||||
|
}
|
||||||
|
--_currentFrame;
|
||||||
|
|
||||||
|
if (_currentFrame == _recording->getFrameNumber() - 1) {
|
||||||
|
--_currentFrame;
|
||||||
|
_frameInterpolationFactor = 1.0f;
|
||||||
|
} else {
|
||||||
|
qint64 currentTimestamps = _recording->getFrameTimestamp(_currentFrame);
|
||||||
|
qint64 nextTimestamps = _recording->getFrameTimestamp(_currentFrame + 1);
|
||||||
|
_frameInterpolationFactor = (float)(elapsed - currentTimestamps) /
|
||||||
|
(float)(nextTimestamps - currentTimestamps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_frameInterpolationFactor < 0.0f || _frameInterpolationFactor > 1.0f) {
|
||||||
|
_frameInterpolationFactor = 0.0f;
|
||||||
|
qDebug() << "Invalid frame interpolation value: overriding";
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,11 @@ public slots:
|
||||||
void loadRecording(RecordingPointer recording);
|
void loadRecording(RecordingPointer recording);
|
||||||
void play();
|
void play();
|
||||||
|
|
||||||
void setCurrentFrame(int currentFrame);
|
void setCurrentFrame(unsigned int currentFrame);
|
||||||
void setCurrentTime(qint64 currentTime);
|
void setCurrentTime(unsigned int currentTime);
|
||||||
|
|
||||||
|
void setVolume(float volume);
|
||||||
|
void setAudioOffset(int audioOffset);
|
||||||
|
|
||||||
void setPlayFromCurrentLocation(bool playFromCurrentPosition);
|
void setPlayFromCurrentLocation(bool playFromCurrentPosition);
|
||||||
void setLoop(bool loop) { _loop = loop; }
|
void setLoop(bool loop) { _loop = loop; }
|
||||||
|
@ -61,18 +64,20 @@ private:
|
||||||
void setAudionInjectorPosition();
|
void setAudionInjectorPosition();
|
||||||
bool computeCurrentFrame();
|
bool computeCurrentFrame();
|
||||||
|
|
||||||
QElapsedTimer _timer;
|
AvatarData* _avatar;
|
||||||
RecordingPointer _recording;
|
RecordingPointer _recording;
|
||||||
int _currentFrame;
|
int _currentFrame;
|
||||||
|
float _frameInterpolationFactor;
|
||||||
int _pausedFrame;
|
int _pausedFrame;
|
||||||
qint64 _timerOffset;
|
|
||||||
|
|
||||||
|
QElapsedTimer _timer;
|
||||||
|
int _timerOffset;
|
||||||
|
int _audioOffset;
|
||||||
|
|
||||||
|
QThread* _audioThread;
|
||||||
QSharedPointer<AudioInjector> _injector;
|
QSharedPointer<AudioInjector> _injector;
|
||||||
AudioInjectorOptions _options;
|
AudioInjectorOptions _options;
|
||||||
|
|
||||||
AvatarData* _avatar;
|
|
||||||
QThread* _audioThread;
|
|
||||||
|
|
||||||
RecordingContext _currentContext;
|
RecordingContext _currentContext;
|
||||||
bool _playFromCurrentPosition;
|
bool _playFromCurrentPosition;
|
||||||
bool _loop;
|
bool _loop;
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QtCore/QDataStream>
|
#include <QtCore/QDataStream>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
|
@ -25,9 +27,14 @@
|
||||||
|
|
||||||
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
|
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
|
||||||
|
|
||||||
AccountManager& AccountManager::getInstance() {
|
AccountManager& AccountManager::getInstance(bool forceReset) {
|
||||||
static AccountManager sharedInstance;
|
static std::auto_ptr<AccountManager> sharedInstance(new AccountManager());
|
||||||
return sharedInstance;
|
|
||||||
|
if (forceReset) {
|
||||||
|
sharedInstance.reset(new AccountManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *sharedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(OAuthAccessToken)
|
Q_DECLARE_METATYPE(OAuthAccessToken)
|
||||||
|
|
|
@ -42,7 +42,7 @@ const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
|
||||||
class AccountManager : public QObject {
|
class AccountManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static AccountManager& getInstance();
|
static AccountManager& getInstance(bool forceReset = false);
|
||||||
|
|
||||||
void authenticatedRequest(const QString& path,
|
void authenticatedRequest(const QString& path,
|
||||||
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
||||||
|
|
|
@ -35,29 +35,32 @@ const char SOLO_NODE_TYPES[2] = {
|
||||||
|
|
||||||
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io");
|
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) {
|
LimitedNodeList* LimitedNodeList::createInstance(unsigned short socketListenPort, unsigned short dtlsPort) {
|
||||||
if (!_sharedInstance) {
|
|
||||||
NodeType::init();
|
NodeType::init();
|
||||||
|
|
||||||
_sharedInstance = new LimitedNodeList(socketListenPort, dtlsPort);
|
if (_sharedInstance.get()) {
|
||||||
|
qDebug() << "LimitedNodeList called with existing instance." <<
|
||||||
|
"Releasing auto_ptr, deleting existing instance and creating a new one.";
|
||||||
|
|
||||||
|
delete _sharedInstance.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sharedInstance = std::auto_ptr<LimitedNodeList>(new LimitedNodeList(socketListenPort, dtlsPort));
|
||||||
|
|
||||||
// register the SharedNodePointer meta-type for signals/slots
|
// register the SharedNodePointer meta-type for signals/slots
|
||||||
qRegisterMetaType<SharedNodePointer>();
|
qRegisterMetaType<SharedNodePointer>();
|
||||||
} else {
|
|
||||||
qDebug("LimitedNodeList createInstance called with existing instance.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _sharedInstance;
|
return _sharedInstance.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
LimitedNodeList* LimitedNodeList::getInstance() {
|
LimitedNodeList* LimitedNodeList::getInstance() {
|
||||||
if (!_sharedInstance) {
|
if (!_sharedInstance.get()) {
|
||||||
qDebug("LimitedNodeList getInstance called before call to createInstance. Returning NULL pointer.");
|
qDebug("LimitedNodeList getInstance called before call to createInstance. Returning NULL pointer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return _sharedInstance;
|
return _sharedInstance.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <unistd.h> // not on windows, not needed for mac or windows
|
#include <unistd.h> // not on windows, not needed for mac or windows
|
||||||
|
@ -118,7 +119,7 @@ signals:
|
||||||
void nodeKilled(SharedNodePointer);
|
void nodeKilled(SharedNodePointer);
|
||||||
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
|
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
|
||||||
protected:
|
protected:
|
||||||
static LimitedNodeList* _sharedInstance;
|
static std::auto_ptr<LimitedNodeList> _sharedInstance;
|
||||||
|
|
||||||
LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort);
|
LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort);
|
||||||
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||||
|
|
|
@ -24,30 +24,31 @@
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
#include "UUID.h"
|
#include "UUID.h"
|
||||||
|
|
||||||
NodeList* NodeList::_sharedInstance = NULL;
|
|
||||||
|
|
||||||
NodeList* NodeList::createInstance(char ownerType, unsigned short socketListenPort, unsigned short dtlsPort) {
|
NodeList* NodeList::createInstance(char ownerType, unsigned short socketListenPort, unsigned short dtlsPort) {
|
||||||
if (!_sharedInstance) {
|
|
||||||
NodeType::init();
|
NodeType::init();
|
||||||
|
|
||||||
_sharedInstance = new NodeList(ownerType, socketListenPort, dtlsPort);
|
if (_sharedInstance.get()) {
|
||||||
LimitedNodeList::_sharedInstance = _sharedInstance;
|
qDebug() << "NodeList called with existing instance." <<
|
||||||
|
"Releasing auto_ptr, deleting existing instance and creating a new one.";
|
||||||
|
|
||||||
|
delete _sharedInstance.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sharedInstance = std::auto_ptr<LimitedNodeList>(new NodeList(ownerType, socketListenPort, dtlsPort));
|
||||||
|
|
||||||
// register the SharedNodePointer meta-type for signals/slots
|
// register the SharedNodePointer meta-type for signals/slots
|
||||||
qRegisterMetaType<SharedNodePointer>();
|
qRegisterMetaType<SharedNodePointer>();
|
||||||
} else {
|
|
||||||
qDebug("NodeList createInstance called with existing instance.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _sharedInstance;
|
return static_cast<NodeList*>(_sharedInstance.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeList* NodeList::getInstance() {
|
NodeList* NodeList::getInstance() {
|
||||||
if (!_sharedInstance) {
|
if (!_sharedInstance.get()) {
|
||||||
qDebug("NodeList getInstance called before call to createInstance. Returning NULL pointer.");
|
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) :
|
NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
||||||
|
|
|
@ -84,8 +84,6 @@ public slots:
|
||||||
signals:
|
signals:
|
||||||
void limitOfSilentDomainCheckInsReached();
|
void limitOfSilentDomainCheckInsReached();
|
||||||
private:
|
private:
|
||||||
static NodeList* _sharedInstance;
|
|
||||||
|
|
||||||
NodeList(char ownerType, unsigned short socketListenPort, unsigned short dtlsListenPort);
|
NodeList(char ownerType, unsigned short socketListenPort, unsigned short dtlsListenPort);
|
||||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
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
|
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||||
|
|
|
@ -87,43 +87,48 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
|
||||||
return mergedMap;
|
return mergedMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap HifiConfigVariantMap::mergeMasterConfigWithUserConfig(const QStringList& argumentList) {
|
HifiConfigVariantMap::HifiConfigVariantMap() :
|
||||||
|
_userConfigFilename(),
|
||||||
|
_masterConfig(),
|
||||||
|
_userConfig(),
|
||||||
|
_mergedConfig()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentList) {
|
||||||
// check if there is a master config file
|
// check if there is a master config file
|
||||||
const QString MASTER_CONFIG_FILE_OPTION = "--master-config";
|
const QString MASTER_CONFIG_FILE_OPTION = "--master-config";
|
||||||
|
|
||||||
QVariantMap configVariantMap;
|
|
||||||
|
|
||||||
int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION);
|
int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION);
|
||||||
if (masterConfigIndex != -1) {
|
if (masterConfigIndex != -1) {
|
||||||
QString masterConfigFilepath = argumentList[masterConfigIndex + 1];
|
QString masterConfigFilepath = argumentList[masterConfigIndex + 1];
|
||||||
|
|
||||||
mergeMapWithJSONFile(configVariantMap, masterConfigFilepath);
|
loadMapFromJSONFile(_masterConfig, masterConfigFilepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge the existing configVariantMap with the user config file
|
// load the user config
|
||||||
mergeMapWithJSONFile(configVariantMap, userConfigFilepath(argumentList));
|
|
||||||
|
|
||||||
return configVariantMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString HifiConfigVariantMap::userConfigFilepath(const QStringList& argumentList) {
|
|
||||||
// we've loaded up the master config file, now fill in anything it didn't have with the user config file
|
|
||||||
const QString USER_CONFIG_FILE_OPTION = "--user-config";
|
const QString USER_CONFIG_FILE_OPTION = "--user-config";
|
||||||
|
|
||||||
int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION);
|
int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION);
|
||||||
QString userConfigFilepath;
|
|
||||||
if (userConfigIndex != -1) {
|
if (userConfigIndex != -1) {
|
||||||
userConfigFilepath = argumentList[userConfigIndex + 1];
|
_userConfigFilename = argumentList[userConfigIndex + 1];
|
||||||
} else {
|
} else {
|
||||||
userConfigFilepath = QString("%1/%2/%3/config.json").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation),
|
_userConfigFilename = QString("%1/%2/%3/config.json").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation),
|
||||||
QCoreApplication::organizationName(),
|
QCoreApplication::organizationName(),
|
||||||
QCoreApplication::applicationName());
|
QCoreApplication::applicationName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return userConfigFilepath;
|
loadMapFromJSONFile(_userConfig, _userConfigFilename);
|
||||||
|
|
||||||
|
// the merged config is initially matched to the master config
|
||||||
|
_mergedConfig = _masterConfig;
|
||||||
|
|
||||||
|
// then we merge in anything missing from the user config
|
||||||
|
addMissingValuesToExistingMap(_mergedConfig, _userConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HifiConfigVariantMap::mergeMapWithJSONFile(QVariantMap& existingMap, const QString& filename) {
|
void HifiConfigVariantMap::loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename) {
|
||||||
QFile configFile(filename);
|
QFile configFile(filename);
|
||||||
|
|
||||||
if (configFile.exists()) {
|
if (configFile.exists()) {
|
||||||
|
@ -131,12 +136,7 @@ void HifiConfigVariantMap::mergeMapWithJSONFile(QVariantMap& existingMap, const
|
||||||
configFile.open(QIODevice::ReadOnly);
|
configFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
||||||
|
|
||||||
if (existingMap.isEmpty()) {
|
|
||||||
existingMap = configDocument.toVariant().toMap();
|
existingMap = configDocument.toVariant().toMap();
|
||||||
} else {
|
|
||||||
addMissingValuesToExistingMap(existingMap, configDocument.toVariant().toMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Could not find JSON config file at" << filename;
|
qDebug() << "Could not find JSON config file at" << filename;
|
||||||
|
|
|
@ -17,11 +17,24 @@
|
||||||
class HifiConfigVariantMap {
|
class HifiConfigVariantMap {
|
||||||
public:
|
public:
|
||||||
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
||||||
static QVariantMap mergeMasterConfigWithUserConfig(const QStringList& argumentList);
|
|
||||||
static QString userConfigFilepath(const QStringList& argumentList);
|
HifiConfigVariantMap();
|
||||||
|
void loadMasterAndUserConfig(const QStringList& argumentList);
|
||||||
|
|
||||||
|
const QVariantMap& getMasterConfig() const { return _masterConfig; }
|
||||||
|
QVariantMap& getUserConfig() { return _userConfig; }
|
||||||
|
QVariantMap& getMergedConfig() { return _mergedConfig; }
|
||||||
|
|
||||||
|
const QString& getUserConfigFilename() const { return _userConfigFilename; }
|
||||||
private:
|
private:
|
||||||
static void mergeMapWithJSONFile(QVariantMap& existingMap, const QString& filename);
|
QString _userConfigFilename;
|
||||||
static void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
|
|
||||||
|
QVariantMap _masterConfig;
|
||||||
|
QVariantMap _userConfig;
|
||||||
|
QVariantMap _mergedConfig;
|
||||||
|
|
||||||
|
void loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename);
|
||||||
|
void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
|
||||||
};
|
};
|
||||||
|
|
||||||
const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath);
|
const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath);
|
||||||
|
|
Loading…
Reference in a new issue