mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 10:26:14 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into removeParticles
This commit is contained in:
commit
fc507850be
102 changed files with 2687 additions and 3933 deletions
|
@ -20,8 +20,13 @@ if (WIN32)
|
|||
# set path for Microsoft SDKs
|
||||
# if get build error about missing 'glu32' this path is likely wrong
|
||||
# Uncomment the line with 8.1 if running Windows 8.1
|
||||
#set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86")
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
if (MSVC10)
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
elseif (MSVC12)
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86 ")
|
||||
endif ()
|
||||
message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH})
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <mmintrin.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
|
@ -46,6 +45,7 @@
|
|||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
|
@ -57,14 +57,14 @@
|
|||
#include "AvatarAudioStream.h"
|
||||
#include "InjectedAudioStream.h"
|
||||
|
||||
|
||||
|
||||
#include "AudioMixer.h"
|
||||
|
||||
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_ENV_GROUP_KEY = "audio_env";
|
||||
const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer";
|
||||
|
||||
void attachNewNodeDataToNode(Node *newNode) {
|
||||
if (!newNode->getLinkedData()) {
|
||||
|
@ -87,8 +87,6 @@ AudioMixer::AudioMixer(const QByteArray& packet) :
|
|||
_numStatFrames(0),
|
||||
_sumListeners(0),
|
||||
_sumMixes(0),
|
||||
_sourceUnattenuatedZone(NULL),
|
||||
_listenerUnattenuatedZone(NULL),
|
||||
_lastPerSecondCallbackTime(usecTimestampNow()),
|
||||
_sendAudioStreamStats(false),
|
||||
_datagramsReadPerCallStats(0, READ_DATAGRAMS_STATS_WINDOW_SECONDS),
|
||||
|
@ -100,18 +98,13 @@ AudioMixer::AudioMixer(const QByteArray& packet) :
|
|||
// we will soon find a better common home for these audio-related constants
|
||||
}
|
||||
|
||||
AudioMixer::~AudioMixer() {
|
||||
delete _sourceUnattenuatedZone;
|
||||
delete _listenerUnattenuatedZone;
|
||||
}
|
||||
|
||||
const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f;
|
||||
const float RADIUS_OF_HEAD = 0.076f;
|
||||
|
||||
int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData,
|
||||
const QUuid& streamUUID,
|
||||
PositionalAudioStream* streamToAdd,
|
||||
AvatarAudioStream* listeningNodeStream) {
|
||||
const QUuid& streamUUID,
|
||||
PositionalAudioStream* streamToAdd,
|
||||
AvatarAudioStream* listeningNodeStream) {
|
||||
// If repetition with fade is enabled:
|
||||
// If streamToAdd could not provide a frame (it was starved), then we'll mix its previously-mixed frame
|
||||
// This is preferable to not mixing it at all since that's equivalent to inserting silence.
|
||||
|
@ -120,9 +113,9 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l
|
|||
// This improves the perceived quality of the audio slightly.
|
||||
|
||||
bool showDebug = false; // (randFloat() < 0.05f);
|
||||
|
||||
|
||||
float repeatedFrameFadeFactor = 1.0f;
|
||||
|
||||
|
||||
if (!streamToAdd->lastPopSucceeded()) {
|
||||
if (_streamSettings._repetitionWithFade && !streamToAdd->getLastPopOutput().isNull()) {
|
||||
// reptition with fade is enabled, and we do have a valid previous frame to repeat.
|
||||
|
@ -135,21 +128,19 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// at this point, we know streamToAdd's last pop output is valid
|
||||
|
||||
|
||||
// if the frame we're about to mix is silent, bail
|
||||
if (streamToAdd->getLastPopOutputLoudness() == 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float bearingRelativeAngleToSource = 0.0f;
|
||||
float attenuationCoefficient = 1.0f;
|
||||
int numSamplesDelay = 0;
|
||||
float weakChannelAmplitudeRatio = 1.0f;
|
||||
|
||||
bool shouldDistanceAttenuate = true;
|
||||
|
||||
// Is the source that I am mixing my own?
|
||||
bool sourceIsSelf = (streamToAdd == listeningNodeStream);
|
||||
|
||||
|
@ -169,10 +160,6 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l
|
|||
|
||||
++_sumMixes;
|
||||
|
||||
if (streamToAdd->getListenerUnattenuatedZone()) {
|
||||
shouldDistanceAttenuate = !streamToAdd->getListenerUnattenuatedZone()->contains(listeningNodeStream->getPosition());
|
||||
}
|
||||
|
||||
if (streamToAdd->getType() == PositionalAudioStream::Injector) {
|
||||
attenuationCoefficient *= reinterpret_cast<InjectedAudioStream*>(streamToAdd)->getAttenuationRatio();
|
||||
if (showDebug) {
|
||||
|
@ -208,10 +195,19 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l
|
|||
attenuationCoefficient *= offAxisCoefficient;
|
||||
}
|
||||
|
||||
if (shouldDistanceAttenuate && (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE)) {
|
||||
float attenuationPerDoublingInDistance = _attenuationPerDoublingInDistance;
|
||||
for (int i = 0; i < _zonesSettings.length(); ++i) {
|
||||
if (_audioZones[_zonesSettings[i].source].contains(streamToAdd->getPosition()) &&
|
||||
_audioZones[_zonesSettings[i].listener].contains(listeningNodeStream->getPosition())) {
|
||||
attenuationPerDoublingInDistance = _zonesSettings[i].coefficient;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) {
|
||||
// calculate the distance coefficient using the distance to this node
|
||||
float distanceCoefficient = 1 - (logf(distanceBetween / ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f)
|
||||
* _attenuationPerDoublingInDistance);
|
||||
* attenuationPerDoublingInDistance);
|
||||
|
||||
if (distanceCoefficient < 0) {
|
||||
distanceCoefficient = 0;
|
||||
|
@ -637,120 +633,7 @@ void AudioMixer::run() {
|
|||
const QJsonObject& settingsObject = domainHandler.getSettingsObject();
|
||||
|
||||
// check the settings object to see if we have anything we can parse out
|
||||
const QString AUDIO_GROUP_KEY = "audio";
|
||||
|
||||
if (settingsObject.contains(AUDIO_GROUP_KEY)) {
|
||||
QJsonObject audioGroupObject = settingsObject[AUDIO_GROUP_KEY].toObject();
|
||||
|
||||
// check the payload to see if we have asked for dynamicJitterBuffer support
|
||||
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer";
|
||||
_streamSettings._dynamicJitterBuffers = audioGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
|
||||
if (_streamSettings._dynamicJitterBuffers) {
|
||||
qDebug() << "Enable dynamic jitter buffers.";
|
||||
} else {
|
||||
qDebug() << "Dynamic jitter buffers disabled.";
|
||||
}
|
||||
|
||||
bool ok;
|
||||
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames";
|
||||
_streamSettings._staticDesiredJitterBufferFrames = audioGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES;
|
||||
}
|
||||
qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames;
|
||||
|
||||
const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max_frames_over_desired";
|
||||
_streamSettings._maxFramesOverDesired = audioGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED;
|
||||
}
|
||||
qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired;
|
||||
|
||||
const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc";
|
||||
_streamSettings._useStDevForJitterCalc = audioGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool();
|
||||
if (_streamSettings._useStDevForJitterCalc) {
|
||||
qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled";
|
||||
} else {
|
||||
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";
|
||||
_streamSettings._windowStarveThreshold = audioGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD;
|
||||
}
|
||||
qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold;
|
||||
|
||||
const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window_seconds_for_desired_calc_on_too_many_starves";
|
||||
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES;
|
||||
}
|
||||
qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds";
|
||||
|
||||
const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window_seconds_for_desired_reduction";
|
||||
_streamSettings._windowSecondsForDesiredReduction = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION;
|
||||
}
|
||||
qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds";
|
||||
|
||||
const QString REPETITION_WITH_FADE_JSON_KEY = "repetition_with_fade";
|
||||
_streamSettings._repetitionWithFade = audioGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool();
|
||||
if (_streamSettings._repetitionWithFade) {
|
||||
qDebug() << "Repetition with fade enabled";
|
||||
} else {
|
||||
qDebug() << "Repetition with fade disabled";
|
||||
}
|
||||
|
||||
const QString PRINT_STREAM_STATS_JSON_KEY = "print_stream_stats";
|
||||
_printStreamStats = audioGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool();
|
||||
if (_printStreamStats) {
|
||||
qDebug() << "Stream stats will be printed to stdout";
|
||||
}
|
||||
|
||||
const QString FILTER_KEY = "enable_filter";
|
||||
if (audioGroupObject[FILTER_KEY].isBool()) {
|
||||
_enableFilter = audioGroupObject[FILTER_KEY].toBool();
|
||||
}
|
||||
if (_enableFilter) {
|
||||
qDebug() << "Filter enabled";
|
||||
}
|
||||
|
||||
const QString UNATTENUATED_ZONE_KEY = "unattenuated_zone";
|
||||
|
||||
QString unattenuatedZoneString = audioGroupObject[UNATTENUATED_ZONE_KEY].toString();
|
||||
if (!unattenuatedZoneString.isEmpty()) {
|
||||
QStringList zoneStringList = unattenuatedZoneString.split(',');
|
||||
|
||||
glm::vec3 sourceCorner(zoneStringList[0].toFloat(), zoneStringList[1].toFloat(), zoneStringList[2].toFloat());
|
||||
glm::vec3 sourceDimensions(zoneStringList[3].toFloat(), zoneStringList[4].toFloat(), zoneStringList[5].toFloat());
|
||||
|
||||
glm::vec3 listenerCorner(zoneStringList[6].toFloat(), zoneStringList[7].toFloat(), zoneStringList[8].toFloat());
|
||||
glm::vec3 listenerDimensions(zoneStringList[9].toFloat(), zoneStringList[10].toFloat(), zoneStringList[11].toFloat());
|
||||
|
||||
_sourceUnattenuatedZone = new AABox(sourceCorner, sourceDimensions);
|
||||
_listenerUnattenuatedZone = new AABox(listenerCorner, listenerDimensions);
|
||||
|
||||
glm::vec3 sourceCenter = _sourceUnattenuatedZone->calcCenter();
|
||||
glm::vec3 destinationCenter = _listenerUnattenuatedZone->calcCenter();
|
||||
|
||||
qDebug() << "There is an unattenuated zone with source center at"
|
||||
<< QString("%1, %2, %3").arg(sourceCenter.x).arg(sourceCenter.y).arg(sourceCenter.z);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
parseSettingsObject(settingsObject);
|
||||
|
||||
int nextFrame = 0;
|
||||
QElapsedTimer timer;
|
||||
|
@ -829,7 +712,7 @@ void AudioMixer::run() {
|
|||
// this function will attempt to pop a frame from each audio stream.
|
||||
// a pointer to the popped data is stored as a member in InboundAudioStream.
|
||||
// That's how the popped audio data will be read for mixing (but only if the pop was successful)
|
||||
nodeData->checkBuffersBeforeFrameSend(_sourceUnattenuatedZone, _listenerUnattenuatedZone);
|
||||
nodeData->checkBuffersBeforeFrameSend();
|
||||
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket()
|
||||
&& nodeData->getAvatarAudioStream()) {
|
||||
|
@ -982,3 +865,175 @@ QString AudioMixer::getReadPendingDatagramsHashMatchTimeStatsString() const {
|
|||
+ " prct_time_in_hashmatch_1s: " + QString::number(_timeSpentPerHashMatchCallStats.getLastCompleteIntervalStats().getSum() / USECS_PER_SECOND * 100.0, 'f', 6) + "%";
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) {
|
||||
if (settingsObject.contains(AUDIO_BUFFER_GROUP_KEY)) {
|
||||
QJsonObject audioBufferGroupObject = settingsObject[AUDIO_BUFFER_GROUP_KEY].toObject();
|
||||
|
||||
// check the payload to see if we have asked for dynamicJitterBuffer support
|
||||
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer";
|
||||
_streamSettings._dynamicJitterBuffers = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
|
||||
if (_streamSettings._dynamicJitterBuffers) {
|
||||
qDebug() << "Enable dynamic jitter buffers.";
|
||||
} else {
|
||||
qDebug() << "Dynamic jitter buffers disabled.";
|
||||
}
|
||||
|
||||
bool ok;
|
||||
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames";
|
||||
_streamSettings._staticDesiredJitterBufferFrames = audioBufferGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES;
|
||||
}
|
||||
qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames;
|
||||
|
||||
const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max_frames_over_desired";
|
||||
_streamSettings._maxFramesOverDesired = audioBufferGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED;
|
||||
}
|
||||
qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired;
|
||||
|
||||
const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc";
|
||||
_streamSettings._useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool();
|
||||
if (_streamSettings._useStDevForJitterCalc) {
|
||||
qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled";
|
||||
} else {
|
||||
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";
|
||||
_streamSettings._windowStarveThreshold = audioBufferGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD;
|
||||
}
|
||||
qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold;
|
||||
|
||||
const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window_seconds_for_desired_calc_on_too_many_starves";
|
||||
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES;
|
||||
}
|
||||
qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds";
|
||||
|
||||
const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window_seconds_for_desired_reduction";
|
||||
_streamSettings._windowSecondsForDesiredReduction = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok);
|
||||
if (!ok) {
|
||||
_streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION;
|
||||
}
|
||||
qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds";
|
||||
|
||||
const QString REPETITION_WITH_FADE_JSON_KEY = "repetition_with_fade";
|
||||
_streamSettings._repetitionWithFade = audioBufferGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool();
|
||||
if (_streamSettings._repetitionWithFade) {
|
||||
qDebug() << "Repetition with fade enabled";
|
||||
} else {
|
||||
qDebug() << "Repetition with fade disabled";
|
||||
}
|
||||
|
||||
const QString PRINT_STREAM_STATS_JSON_KEY = "print_stream_stats";
|
||||
_printStreamStats = audioBufferGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool();
|
||||
if (_printStreamStats) {
|
||||
qDebug() << "Stream stats will be printed to stdout";
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsObject.contains(AUDIO_ENV_GROUP_KEY)) {
|
||||
QJsonObject audioEnvGroupObject = settingsObject[AUDIO_ENV_GROUP_KEY].toObject();
|
||||
|
||||
const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation_per_doubling_in_distance";
|
||||
if (audioEnvGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].isString()) {
|
||||
bool ok = false;
|
||||
float attenuation = audioEnvGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok);
|
||||
if (ok) {
|
||||
_attenuationPerDoublingInDistance = attenuation;
|
||||
qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance;
|
||||
}
|
||||
}
|
||||
|
||||
const QString FILTER_KEY = "enable_filter";
|
||||
if (audioEnvGroupObject[FILTER_KEY].isBool()) {
|
||||
_enableFilter = audioEnvGroupObject[FILTER_KEY].toBool();
|
||||
}
|
||||
if (_enableFilter) {
|
||||
qDebug() << "Filter enabled";
|
||||
}
|
||||
|
||||
const QString AUDIO_ZONES = "zones";
|
||||
if (audioEnvGroupObject[AUDIO_ZONES].isObject()) {
|
||||
const QJsonObject& zones = audioEnvGroupObject[AUDIO_ZONES].toObject();
|
||||
|
||||
const QString X_RANGE = "x_range";
|
||||
const QString Y_RANGE = "y_range";
|
||||
const QString Z_RANGE = "z_range";
|
||||
foreach (const QString& zone, zones.keys()) {
|
||||
QJsonObject zoneObject = zones[zone].toObject();
|
||||
|
||||
if (zoneObject.contains(X_RANGE) && zoneObject.contains(Y_RANGE) && zoneObject.contains(Z_RANGE)) {
|
||||
QStringList xRange = zoneObject.value(X_RANGE).toString().split("-", QString::SkipEmptyParts);
|
||||
QStringList yRange = zoneObject.value(Y_RANGE).toString().split("-", QString::SkipEmptyParts);
|
||||
QStringList zRange = zoneObject.value(Z_RANGE).toString().split("-", QString::SkipEmptyParts);
|
||||
|
||||
if (xRange.size() == 2 && yRange.size() == 2 && zRange.size() == 2) {
|
||||
float xMin, xMax, yMin, yMax, zMin, zMax;
|
||||
bool ok, allOk = true;
|
||||
xMin = xRange[0].toFloat(&ok);
|
||||
allOk &= ok;
|
||||
xMax = xRange[1].toFloat(&ok);
|
||||
allOk &= ok;
|
||||
yMin = yRange[0].toFloat(&ok);
|
||||
allOk &= ok;
|
||||
yMax = yRange[1].toFloat(&ok);
|
||||
allOk &= ok;
|
||||
zMin = zRange[0].toFloat(&ok);
|
||||
allOk &= ok;
|
||||
zMax = zRange[1].toFloat(&ok);
|
||||
allOk &= ok;
|
||||
|
||||
if (allOk) {
|
||||
glm::vec3 corner(xMin, yMin, zMin);
|
||||
glm::vec3 dimensions(xMax - xMin, yMax - yMin, zMax - zMin);
|
||||
AABox zoneAABox(corner, dimensions);
|
||||
_audioZones.insert(zone, zoneAABox);
|
||||
qDebug() << "Added zone:" << zone << "(corner:" << corner
|
||||
<< ", dimensions:" << dimensions << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QString ATTENUATION_COEFFICIENTS = "attenuation_coefficients";
|
||||
if (audioEnvGroupObject[ATTENUATION_COEFFICIENTS].isArray()) {
|
||||
const QJsonArray& coefficients = audioEnvGroupObject[ATTENUATION_COEFFICIENTS].toArray();
|
||||
|
||||
const QString SOURCE = "source";
|
||||
const QString LISTENER = "listener";
|
||||
const QString COEFFICIENT = "coefficient";
|
||||
for (int i = 0; i < coefficients.count(); ++i) {
|
||||
QJsonObject coefficientObject = coefficients[i].toObject();
|
||||
|
||||
if (coefficientObject.contains(SOURCE) &&
|
||||
coefficientObject.contains(LISTENER) &&
|
||||
coefficientObject.contains(COEFFICIENT)) {
|
||||
|
||||
ZonesSettings settings;
|
||||
|
||||
bool ok;
|
||||
settings.source = coefficientObject.value(SOURCE).toString();
|
||||
settings.listener = coefficientObject.value(LISTENER).toString();
|
||||
settings.coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok);
|
||||
|
||||
if (ok && settings.coefficient >= 0.0f && settings.coefficient <= 1.0f &&
|
||||
_audioZones.contains(settings.source) && _audioZones.contains(settings.listener)) {
|
||||
|
||||
_zonesSettings.push_back(settings);
|
||||
qDebug() << "Added Coefficient:" << settings.source << settings.listener << settings.coefficient;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ class AudioMixer : public ThreadedAssignment {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AudioMixer(const QByteArray& packet);
|
||||
~AudioMixer();
|
||||
public slots:
|
||||
/// threaded run of assignment
|
||||
void run();
|
||||
|
@ -66,6 +65,8 @@ private:
|
|||
QString getReadPendingDatagramsTimeStatsString() const;
|
||||
QString getReadPendingDatagramsHashMatchTimeStatsString() const;
|
||||
|
||||
void parseSettingsObject(const QJsonObject& settingsObject);
|
||||
|
||||
float _trailingSleepRatio;
|
||||
float _minAudibilityThreshold;
|
||||
float _performanceThrottlingRatio;
|
||||
|
@ -73,8 +74,14 @@ private:
|
|||
int _numStatFrames;
|
||||
int _sumListeners;
|
||||
int _sumMixes;
|
||||
AABox* _sourceUnattenuatedZone;
|
||||
AABox* _listenerUnattenuatedZone;
|
||||
|
||||
QHash<QString, AABox> _audioZones;
|
||||
struct ZonesSettings {
|
||||
QString source;
|
||||
QString listener;
|
||||
float coefficient;
|
||||
};
|
||||
QVector<ZonesSettings> _zonesSettings;
|
||||
|
||||
static InboundAudioStream::Settings _streamSettings;
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void AudioMixerClientData::checkBuffersBeforeFrameSend(AABox* checkSourceZone, AABox* listenerZone) {
|
||||
void AudioMixerClientData::checkBuffersBeforeFrameSend() {
|
||||
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
|
||||
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
|
||||
PositionalAudioStream* stream = i.value();
|
||||
|
@ -114,12 +114,6 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(AABox* checkSourceZone, A
|
|||
if (stream->popFrames(1, true) > 0) {
|
||||
stream->updateLastPopOutputLoudnessAndTrailingLoudness();
|
||||
}
|
||||
|
||||
if (checkSourceZone && checkSourceZone->contains(stream->getPosition())) {
|
||||
stream->setListenerUnattenuatedZone(listenerZone);
|
||||
} else {
|
||||
stream->setListenerUnattenuatedZone(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
|
||||
int parseData(const QByteArray& packet);
|
||||
|
||||
void checkBuffersBeforeFrameSend(AABox* checkSourceZone, AABox* listenerZone);
|
||||
void checkBuffersBeforeFrameSend();
|
||||
|
||||
void removeDeadInjectedStreams();
|
||||
|
||||
|
|
|
@ -861,8 +861,6 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H
|
|||
}
|
||||
} else if (packetType == PacketTypeJurisdictionRequest) {
|
||||
_jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket);
|
||||
} else if (packetType == PacketTypeSignedTransactionPayment) {
|
||||
handleSignedTransactionPayment(packetType, receivedPacket);
|
||||
} else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
|
||||
_octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket);
|
||||
} else {
|
||||
|
@ -1245,51 +1243,6 @@ QString OctreeServer::getStatusLink() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void OctreeServer::handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram) {
|
||||
// for now we're not verifying that this is actual payment for any octree edits
|
||||
// just use the AccountManager to send it up to the data server and have it redeemed
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE = 72;
|
||||
const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE = 256;
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(packetType);
|
||||
|
||||
// pull out the transaction message in binary
|
||||
QByteArray messageHex = datagram.mid(numBytesPacketHeader, NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE).toHex();
|
||||
// pull out the binary signed message digest
|
||||
QByteArray signatureHex = datagram.mid(numBytesPacketHeader + NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE,
|
||||
NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE).toHex();
|
||||
|
||||
// setup the QJSONObject we are posting
|
||||
QJsonObject postObject;
|
||||
|
||||
const QString TRANSACTION_OBJECT_MESSAGE_KEY = "message";
|
||||
const QString TRANSACTION_OBJECT_SIGNATURE_KEY = "signature";
|
||||
const QString POST_OBJECT_TRANSACTION_KEY = "transaction";
|
||||
|
||||
QJsonObject transactionObject;
|
||||
transactionObject.insert(TRANSACTION_OBJECT_MESSAGE_KEY, QString(messageHex));
|
||||
transactionObject.insert(TRANSACTION_OBJECT_SIGNATURE_KEY, QString(signatureHex));
|
||||
|
||||
postObject.insert(POST_OBJECT_TRANSACTION_KEY, transactionObject);
|
||||
|
||||
// setup our callback params
|
||||
JSONCallbackParameters callbackParameters;
|
||||
callbackParameters.jsonCallbackReceiver = this;
|
||||
callbackParameters.jsonCallbackMethod = "handleSignedTransactionPaymentResponse";
|
||||
|
||||
accountManager.unauthenticatedRequest("/api/v1/transactions/redeem", QNetworkAccessManager::PostOperation,
|
||||
callbackParameters, QJsonDocument(postObject).toJson());
|
||||
|
||||
}
|
||||
|
||||
void OctreeServer::handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject) {
|
||||
// pull the ID to debug the transaction
|
||||
QString transactionIDString = jsonObject["data"].toObject()["transaction"].toObject()["id"].toString();
|
||||
qDebug() << "Redeemed transaction with ID" << transactionIDString << "successfully.";
|
||||
}
|
||||
|
||||
void OctreeServer::sendStatsPacket() {
|
||||
// TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and
|
||||
// send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the
|
||||
|
|
|
@ -127,8 +127,6 @@ public slots:
|
|||
void nodeKilled(SharedNodePointer node);
|
||||
void sendStatsPacket();
|
||||
|
||||
void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject);
|
||||
|
||||
void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle
|
||||
void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
@ -141,7 +139,6 @@ protected:
|
|||
QString getConfiguration();
|
||||
QString getStatusLink();
|
||||
|
||||
void handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram);
|
||||
void setupDatagramProcessingThread();
|
||||
|
||||
int _argc;
|
||||
|
|
|
@ -38,4 +38,18 @@ endif ()
|
|||
# link the shared hifi libraries
|
||||
link_hifi_libraries(embedded-webserver networking shared)
|
||||
|
||||
# find OpenSSL
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
|
||||
# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto
|
||||
message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings."
|
||||
"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.")
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
# append OpenSSL to our list of libraries to link
|
||||
list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}")
|
||||
|
||||
link_shared_dependencies()
|
|
@ -6,7 +6,7 @@
|
|||
{
|
||||
"name": "access_token",
|
||||
"label": "Access Token",
|
||||
"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."
|
||||
"help": "This is an access token generated on the <a href='https://data.highfidelity.io/user/security' target='_blank'>My Security</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",
|
||||
|
@ -59,14 +59,42 @@
|
|||
"type": "password",
|
||||
"help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "allowed_users",
|
||||
"type": "table",
|
||||
"label": "Allowed Users",
|
||||
"help": "List the High Fidelity names for people you want to be able to connect to this domain.<br/>An empty list means everyone.<br/>You can always connect from this machine.",
|
||||
"numbered": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "username",
|
||||
"label": "Username",
|
||||
"can_set": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
"label": "Audio",
|
||||
"name": "audio_env",
|
||||
"label": "Audio Environment",
|
||||
"assignment-types": [0],
|
||||
"settings": [
|
||||
{
|
||||
"name": "attenuation_per_doubling_in_distance",
|
||||
"label": "Default Domain Attenuation",
|
||||
"help": "Factor between 0 and 1.0 (0: No attenuation, 1.0: extreme attenuation)",
|
||||
"placeholder": "0.18",
|
||||
"default": "0.18",
|
||||
"advanced": false
|
||||
},
|
||||
{
|
||||
"name": "enable_filter",
|
||||
"type": "checkbox",
|
||||
"help": "positional audio stream uses lowpass filter",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"name": "zones",
|
||||
"type": "table",
|
||||
|
@ -76,7 +104,7 @@
|
|||
"key": {
|
||||
"name": "name",
|
||||
"label": "Name",
|
||||
"placeholder": "Zone name"
|
||||
"placeholder": "Zone_Name"
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
|
@ -104,7 +132,8 @@
|
|||
"type": "table",
|
||||
"label": "Attenuation Coefficients",
|
||||
"help": "In this table you can set custom attenuation coefficients between audio zones",
|
||||
"numbered": false,
|
||||
"numbered": true,
|
||||
"can_order": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "source",
|
||||
|
@ -125,28 +154,14 @@
|
|||
"placeholder": "0.18"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "enable_filter",
|
||||
"type": "checkbox",
|
||||
"label": "Positional filter",
|
||||
"help": "positional audio stream uses lowpass filter",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"name": "unattenuated_zone",
|
||||
"label": "Unattenuated Zone",
|
||||
"help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)",
|
||||
"placeholder": "no zone"
|
||||
},
|
||||
{
|
||||
"name": "attenuation_per_doubling_in_distance",
|
||||
"label": "Attenuation 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": false
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "audio_buffer",
|
||||
"label": "Audio Buffers",
|
||||
"assignment-types": [0],
|
||||
"settings": [
|
||||
{
|
||||
"name": "dynamic_jitter_buffer",
|
||||
"type": "checkbox",
|
||||
|
|
|
@ -79,12 +79,19 @@ td.buttons {
|
|||
width: 14px;
|
||||
}
|
||||
|
||||
td.buttons .glyphicon {
|
||||
display: block;
|
||||
td .glyphicon {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
td.add-del-buttons .glyphicon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
td.reorder-buttons .glyphicon {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
tr.new-row {
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
|
|
|
@ -8,7 +8,15 @@ var Settings = {
|
|||
ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row',
|
||||
DEL_ROW_BUTTON_CLASS: 'del-row',
|
||||
DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row',
|
||||
MOVE_UP_BUTTON_CLASS: 'move-up',
|
||||
MOVE_UP_SPAN_CLASSES: 'glyphicon glyphicon-chevron-up move-up',
|
||||
MOVE_DOWN_BUTTON_CLASS: 'move-down',
|
||||
MOVE_DOWN_SPAN_CLASSES: 'glyphicon glyphicon-chevron-down move-down',
|
||||
TABLE_BUTTONS_CLASS: 'buttons',
|
||||
ADD_DEL_BUTTONS_CLASS: 'add-del-buttons',
|
||||
ADD_DEL_BUTTONS_CLASSES: 'buttons add-del-buttons',
|
||||
REORDER_BUTTONS_CLASS: 'reorder-buttons',
|
||||
REORDER_BUTTONS_CLASSES: 'buttons reorder-buttons',
|
||||
NEW_ROW_CLASS: 'new-row'
|
||||
};
|
||||
|
||||
|
@ -35,7 +43,9 @@ var viewHelpers = {
|
|||
+ " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + setting_name + "' "
|
||||
|
||||
if (setting.type === 'checkbox') {
|
||||
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||
if (setting.label) {
|
||||
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||
}
|
||||
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
|
||||
form_group += "<label for='" + setting_name + "'>"
|
||||
form_group += "<input type='checkbox'" + common_attrs + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
|
||||
|
@ -45,8 +55,10 @@ var viewHelpers = {
|
|||
form_group += makeTable(setting, setting_name, setting_value);
|
||||
} else {
|
||||
input_type = _.has(setting, 'type') ? setting.type : "text"
|
||||
|
||||
form_group += "<label for='" + setting_name + "' class='" + label_class + "'>" + setting.label + "</label>";
|
||||
|
||||
if (setting.label) {
|
||||
form_group += "<label for='" + setting_name + "' class='" + label_class + "'>" + setting.label + "</label>";
|
||||
}
|
||||
|
||||
if (setting.type === 'select') {
|
||||
form_group += "<select class='form-control' data-hidden-input='" + setting_name + "'>'"
|
||||
|
@ -106,6 +118,14 @@ $(document).ready(function(){
|
|||
$('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){
|
||||
deleteTableRow(this);
|
||||
})
|
||||
|
||||
$('#settings-form').on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){
|
||||
moveTableRow(this, true);
|
||||
})
|
||||
|
||||
$('#settings-form').on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){
|
||||
moveTableRow(this, false);
|
||||
})
|
||||
|
||||
$('#settings-form').on('keypress', 'table input', function(e){
|
||||
if (e.keyCode == 13) {
|
||||
|
@ -116,7 +136,7 @@ $(document).ready(function(){
|
|||
if (sibling.hasClass(Settings.DATA_COL_CLASS)) {
|
||||
// set focus to next input
|
||||
sibling.find('input').focus()
|
||||
} else if (sibling.hasClass(Settings.TABLE_BUTTONS_CLASS)) {
|
||||
} else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
||||
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click()
|
||||
|
||||
// set focus to the first input in the new row
|
||||
|
@ -235,7 +255,11 @@ $('body').on('click', '.save-button', function(e){
|
|||
function makeTable(setting, setting_name, setting_value) {
|
||||
var isArray = !_.has(setting, 'key')
|
||||
|
||||
var html = "<label class='control-label'>" + setting.label + "</label>"
|
||||
if (!isArray && setting.can_order) {
|
||||
setting.can_order = false;
|
||||
}
|
||||
|
||||
var html = (setting.label) ? "<label class='control-label'>" + setting.label + "</label>" : ""
|
||||
html += "<span class='help-block'>" + setting.help + "</span>"
|
||||
html += "<table class='table table-bordered' data-short-name='" + setting.name + "' name='" + setting_name
|
||||
+ "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>"
|
||||
|
@ -255,7 +279,11 @@ function makeTable(setting, setting_name, setting_value) {
|
|||
html += "<td class='data'><strong>" + col.label + "</strong></td>" // Data
|
||||
})
|
||||
|
||||
html += "<td class='buttons'><strong>+/-</strong></td></tr>"
|
||||
if (setting.can_order) {
|
||||
html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES +
|
||||
"><span class='glyphicon glyphicon-sort'></span></td>";
|
||||
}
|
||||
html += "<td class=" + Settings.ADD_DEL_BUTTONS_CLASSES + "></td></tr>"
|
||||
|
||||
// populate rows in the table from existing values
|
||||
var row_num = 1
|
||||
|
@ -275,13 +303,13 @@ function makeTable(setting, setting_name, setting_value) {
|
|||
html += "<td class='" + Settings.DATA_COL_CLASS + "'>"
|
||||
|
||||
if (isArray) {
|
||||
colIsArray = _.isArray(row)
|
||||
colValue = colIsArray ? row : row[col.name]
|
||||
rowIsObject = setting.columns.length > 1
|
||||
colValue = rowIsObject ? row[col.name] : row
|
||||
html += colValue
|
||||
|
||||
// for arrays we add a hidden input to this td so that values can be posted appropriately
|
||||
html += "<input type='hidden' name='" + setting_name + "[" + indexOrName + "]"
|
||||
+ (colIsArray ? "" : "." + col.name) + "' value='" + colValue + "'/>"
|
||||
+ (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>"
|
||||
} else if (row.hasOwnProperty(col.name)) {
|
||||
html += row[col.name]
|
||||
}
|
||||
|
@ -289,7 +317,13 @@ function makeTable(setting, setting_name, setting_value) {
|
|||
html += "</td>"
|
||||
})
|
||||
|
||||
html += "<td class='buttons'><span class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></span></td>"
|
||||
if (setting.can_order) {
|
||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+
|
||||
"'><span class='" + Settings.MOVE_UP_SPAN_CLASSES + "'></span><span class='" +
|
||||
Settings.MOVE_DOWN_SPAN_CLASSES + "'></span></td>"
|
||||
}
|
||||
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
|
||||
"'><span class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></span></td>"
|
||||
html += "</tr>"
|
||||
|
||||
row_num++
|
||||
|
@ -317,11 +351,15 @@ function makeTableInputs(setting) {
|
|||
|
||||
_.each(setting.columns, function(col) {
|
||||
html += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>\
|
||||
<input type='text' class='form-control' placeholder='" + (col.key ? col.key : "") + "' value=''>\
|
||||
<input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' value=''>\
|
||||
</td>"
|
||||
})
|
||||
|
||||
html += "<td class='buttons'><span class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></span></td>"
|
||||
|
||||
if (setting.can_order) {
|
||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'></td>"
|
||||
}
|
||||
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
|
||||
"'><span class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></span></td>"
|
||||
html += "</tr>"
|
||||
|
||||
return html
|
||||
|
@ -414,7 +452,10 @@ function addTableRow(add_glyphicon) {
|
|||
} else {
|
||||
$(element).html(1)
|
||||
}
|
||||
} else if ($(element).hasClass("buttons")) {
|
||||
} else if ($(element).hasClass(Settings.REORDER_BUTTONS_CLASS)) {
|
||||
$(element).html("<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'><span class='" + Settings.MOVE_UP_SPAN_CLASSES +
|
||||
"'></span><span class='" + Settings.MOVE_DOWN_SPAN_CLASSES + "'></span></td>")
|
||||
} else if ($(element).hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
||||
// Change buttons
|
||||
var span = $(element).children("span")
|
||||
span.removeClass(Settings.ADD_ROW_SPAN_CLASSES)
|
||||
|
@ -487,7 +528,7 @@ function deleteTableRow(delete_glyphicon) {
|
|||
|
||||
row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS)
|
||||
row.addClass('empty-array-row')
|
||||
|
||||
|
||||
row.html("<input type='hidden' class='form-control' name='" + table.attr("name").replace('[]', '')
|
||||
+ "' data-changed='true' value=''>");
|
||||
}
|
||||
|
@ -495,7 +536,32 @@ function deleteTableRow(delete_glyphicon) {
|
|||
|
||||
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
||||
badgeSidebarForDifferences($(table))
|
||||
}
|
||||
}
|
||||
|
||||
function moveTableRow(move_glyphicon, move_up) {
|
||||
var row = $(move_glyphicon).closest('tr')
|
||||
|
||||
var table = $(row).closest('table')
|
||||
var isArray = table.data('setting-type') === 'array'
|
||||
if (!isArray) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (move_up) {
|
||||
var prev_row = row.prev()
|
||||
if (prev_row.hasClass(Settings.DATA_ROW_CLASS)) {
|
||||
prev_row.before(row)
|
||||
}
|
||||
} else {
|
||||
var next_row = row.next()
|
||||
if (next_row.hasClass(Settings.DATA_ROW_CLASS)) {
|
||||
next_row.after(row)
|
||||
}
|
||||
}
|
||||
|
||||
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
||||
badgeSidebarForDifferences($(table))
|
||||
}
|
||||
|
||||
function updateDataChangedForSiblingRows(row, forceTrue) {
|
||||
// anytime a new row is added to an array we need to set data-changed for all sibling row inputs to true
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<ul class="nav nav-pills nav-stacked">
|
||||
</ul>
|
||||
|
||||
<button id="advanced-toggle-button" class="btn btn-info">Show advanced</button>
|
||||
<button id="advanced-toggle-button" hidden=true class="btn btn-info">Show advanced</button>
|
||||
<button class="btn btn-success save-button">Save and restart</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,25 +32,32 @@
|
|||
|
||||
<script id="panels-template" type="text/template">
|
||||
<% _.each(descriptions, function(group){ %>
|
||||
<div class="panel panel-default" id="<%- group.name %>">
|
||||
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
|
||||
<% isAdvanced = _.isEmpty(split_settings[0]) %>
|
||||
<% if (isAdvanced) { %>
|
||||
<% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
|
||||
<% } %>
|
||||
<div class="panel panel-default <%- (isAdvanced) ? 'advanced-setting' : '' %>" id="<%- group.name %>">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><%- group.label %></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
|
||||
<% _.each(split_settings[0], function(setting) { %>
|
||||
<%= getFormGroup(group.name, setting, values, false,
|
||||
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||
<% }); %>
|
||||
<% _.each(split_settings[1], function(setting) { %>
|
||||
<%= getFormGroup(group.name, setting, values, true,
|
||||
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||
<% }); %>
|
||||
<% if (!_.isEmpty(split_settings[1])) { %>
|
||||
<% $("#advanced-toggle-button").show() %>
|
||||
<% _.each(split_settings[1], function(setting) { %>
|
||||
<%= getFormGroup(group.name, setting, values, true,
|
||||
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||
<% }); %>
|
||||
<% }%>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</script>
|
||||
|
||||
|
||||
<div id="panels"></div>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
|
@ -44,8 +47,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_oauthProviderURL(),
|
||||
_oauthClientID(),
|
||||
_hostname(),
|
||||
_networkReplyUUIDMap(),
|
||||
_sessionAuthenticationHash(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_settingsManager()
|
||||
|
@ -80,6 +81,9 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
// setup automatic networking settings with data server
|
||||
setupAutomaticNetworking();
|
||||
|
||||
// preload some user public keys so they can connect on first request
|
||||
preloadAllowedUserPublicKeys();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,8 +511,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
}
|
||||
|
||||
const QString ALLOWED_ROLES_CONFIG_KEY = "allowed-roles";
|
||||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::EntityServer
|
||||
<< NodeType::MetavoxelServer;
|
||||
|
@ -517,8 +519,11 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr publicSockAddr, localSockAddr;
|
||||
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr);
|
||||
parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
|
||||
|
||||
QUuid packetUUID = uuidFromPacketHeader(packet);
|
||||
|
||||
|
@ -551,33 +556,20 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
}
|
||||
|
||||
QString connectedUsername;
|
||||
|
||||
if (!isAssignment && !_oauthProviderURL.isEmpty() && _settingsManager.getSettingsMap().contains(ALLOWED_ROLES_CONFIG_KEY)) {
|
||||
// this is an Agent, and we require authentication so we can compare the user's roles to our list of allowed ones
|
||||
if (_sessionAuthenticationHash.contains(packetUUID)) {
|
||||
connectedUsername = _sessionAuthenticationHash.take(packetUUID);
|
||||
if (connectedUsername.isEmpty()) {
|
||||
// we've decided this is a user that isn't allowed in, return out
|
||||
// TODO: provide information to the user so they know why they can't connect
|
||||
return;
|
||||
} else {
|
||||
// we're letting this user in, don't return and remove their UUID from the hash
|
||||
_sessionAuthenticationHash.remove(packetUUID);
|
||||
}
|
||||
} else {
|
||||
// we don't know anything about this client
|
||||
// we have an OAuth provider, ask this interface client to auth against it
|
||||
QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainOAuthRequest);
|
||||
QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append);
|
||||
QUrl authorizationURL = packetUUID.isNull() ? oauthAuthorizationURL() : oauthAuthorizationURL(packetUUID);
|
||||
oauthRequestStream << authorizationURL;
|
||||
|
||||
// send this oauth request datagram back to the client
|
||||
LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr);
|
||||
|
||||
return;
|
||||
}
|
||||
QList<NodeType_t> nodeInterestList;
|
||||
QString username;
|
||||
QByteArray usernameSignature;
|
||||
|
||||
packetStream >> nodeInterestList >> username >> usernameSignature;
|
||||
|
||||
if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr)) {
|
||||
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
|
||||
QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied);
|
||||
|
||||
// send this oauth request datagram back to the client
|
||||
LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType))
|
||||
|
@ -610,15 +602,109 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
}
|
||||
|
||||
// if we have a username from an OAuth connect request, set it on the DomainServerNodeData
|
||||
nodeData->setUsername(connectedUsername);
|
||||
|
||||
nodeData->setUsername(username);
|
||||
nodeData->setSendingSockAddr(senderSockAddr);
|
||||
|
||||
// reply back to the user with a PacketTypeDomainList
|
||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes));
|
||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
||||
|
||||
bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
||||
const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr) {
|
||||
static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
|
||||
if (allowedUsers.count() > 0) {
|
||||
// this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in
|
||||
// we always let in a user who is sending a packet from our local socket or from the localhost address
|
||||
|
||||
if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress()
|
||||
&& senderSockAddr.getAddress() != QHostAddress::LocalHost) {
|
||||
if (allowedUsers.contains(username)) {
|
||||
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||
|
||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||
if (!publicKeyArray.isEmpty()) {
|
||||
// if we do have a public key for the user, check for a signature match
|
||||
|
||||
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
|
||||
|
||||
// first load up the public key into an RSA struct
|
||||
RSA* rsaPublicKey = d2i_RSAPublicKey(NULL, &publicKeyData, publicKeyArray.size());
|
||||
|
||||
if (rsaPublicKey) {
|
||||
QByteArray decryptedArray(RSA_size(rsaPublicKey), 0);
|
||||
int decryptResult = RSA_public_decrypt(usernameSignature.size(),
|
||||
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
|
||||
reinterpret_cast<unsigned char*>(decryptedArray.data()),
|
||||
rsaPublicKey, RSA_PKCS1_PADDING);
|
||||
|
||||
if (decryptResult != -1) {
|
||||
if (username == decryptedArray) {
|
||||
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
||||
|
||||
// free up the public key before we return
|
||||
RSA_free(rsaPublicKey);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Username signature did not match for" << username << "- denying connection.";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection.";
|
||||
}
|
||||
|
||||
// free up the public key, we don't need it anymore
|
||||
RSA_free(rsaPublicKey);
|
||||
} else {
|
||||
// we can't let this user in since we couldn't convert their public key to an RSA key we could use
|
||||
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
||||
}
|
||||
}
|
||||
|
||||
requestUserPublicKey(username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// since we have no allowed user list, let them all in
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DomainServer::preloadAllowedUserPublicKeys() {
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
|
||||
if (allowedUsers.size() > 0) {
|
||||
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not
|
||||
// going to create > 100 requests
|
||||
foreach(const QString& username, allowedUsers) {
|
||||
requestUserPublicKey(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestUserPublicKey(const QString& username) {
|
||||
// even if we have a public key for them right now, request a new one in case it has just changed
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "publicKeyJSONCallback";
|
||||
|
||||
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
|
||||
|
||||
qDebug() << "Requesting public key for user" << username;
|
||||
|
||||
AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username),
|
||||
QNetworkAccessManager::GetOperation, callbackParams);
|
||||
}
|
||||
|
||||
QUrl DomainServer::oauthRedirectURL() {
|
||||
return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort());
|
||||
}
|
||||
|
@ -653,12 +739,9 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) {
|
|||
return authorizationURL;
|
||||
}
|
||||
|
||||
int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr, const QByteArray& packet,
|
||||
const HifiSockAddr& senderSockAddr) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType,
|
||||
HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr,
|
||||
const HifiSockAddr& senderSockAddr) {
|
||||
packetStream >> nodeType;
|
||||
packetStream >> publicSockAddr >> localSockAddr;
|
||||
|
||||
|
@ -925,7 +1008,30 @@ void DomainServer::sendPendingTransactionsToServer() {
|
|||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
// figure out which user this is for
|
||||
|
||||
const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key";
|
||||
QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING);
|
||||
|
||||
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
|
||||
QString username = usernameRegex.cap(1);
|
||||
|
||||
qDebug() << "Storing a public key for user" << username;
|
||||
|
||||
// pull the public key as a QByteArray from this response
|
||||
const QString JSON_DATA_KEY = "data";
|
||||
const QString JSON_PUBLIC_KEY_KEY = "public_key";
|
||||
|
||||
_userPublicKeys[username] =
|
||||
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::transactionJSONCallback(const QJsonObject& data) {
|
||||
|
@ -1095,17 +1201,24 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
|
|||
if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) {
|
||||
NodeType_t throwawayNodeType;
|
||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
||||
|
||||
QDataStream packetStream(receivedPacket);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
|
||||
|
||||
int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress,
|
||||
receivedPacket, senderSockAddr);
|
||||
parseNodeDataFromByteArray(packetStream, throwawayNodeType, nodePublicAddress, nodeLocalAddress,
|
||||
senderSockAddr);
|
||||
|
||||
SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress);
|
||||
SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID,
|
||||
nodePublicAddress, nodeLocalAddress);
|
||||
|
||||
// update last receive to now
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes));
|
||||
QList<NodeType_t> nodeInterestList;
|
||||
packetStream >> nodeInterestList;
|
||||
|
||||
sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestList.toSet());
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1545,13 +1658,6 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
|||
// we've redirected the user back to our homepage
|
||||
return true;
|
||||
|
||||
} else {
|
||||
qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID);
|
||||
|
||||
// insert this to our pending token replies so we can associate the returned access token with the right UUID
|
||||
_networkReplyUUIDMap.insert(tokenReply, stateUUID);
|
||||
|
||||
connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1695,22 +1801,6 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
|
||||
const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token";
|
||||
|
||||
void DomainServer::handleTokenRequestFinished() {
|
||||
QNetworkReply* networkReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply);
|
||||
|
||||
if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) {
|
||||
|
||||
qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID)
|
||||
<< "-" << "requesting profile.";
|
||||
|
||||
QNetworkReply* profileReply = profileRequestGivenTokenReply(networkReply);
|
||||
|
||||
connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished);
|
||||
|
||||
_networkReplyUUIDMap.insert(profileReply, matchingSessionUUID);
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenReply) {
|
||||
// pull the access token from the returned JSON and store it with the matching session UUID
|
||||
|
@ -1719,54 +1809,12 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR
|
|||
|
||||
// fire off a request to get this user's identity so we can see if we will let them in
|
||||
QUrl profileURL = _oauthProviderURL;
|
||||
profileURL.setPath("/api/v1/users/profile");
|
||||
profileURL.setPath("/api/v1/user/profile");
|
||||
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken));
|
||||
|
||||
return NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL));
|
||||
}
|
||||
|
||||
void DomainServer::handleProfileRequestFinished() {
|
||||
QNetworkReply* networkReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply);
|
||||
|
||||
if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) {
|
||||
QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll());
|
||||
|
||||
if (profileJSON.object()["status"].toString() == "success") {
|
||||
// pull the user roles from the response
|
||||
QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray();
|
||||
|
||||
QStringList allowedRolesArray = _settingsManager.getSettingsMap().value(ALLOWED_ROLES_CONFIG_KEY).toStringList();
|
||||
|
||||
QString connectableUsername;
|
||||
QString profileUsername = profileJSON.object()["data"].toObject()["user"].toObject()["username"].toString();
|
||||
|
||||
foreach(const QJsonValue& roleValue, userRolesArray) {
|
||||
if (allowedRolesArray.contains(roleValue.toString())) {
|
||||
// the user has a role that lets them in
|
||||
// set the bool to true and break
|
||||
connectableUsername = profileUsername;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connectableUsername.isEmpty()) {
|
||||
qDebug() << "User" << profileUsername << "with session UUID"
|
||||
<< uuidStringWithoutCurlyBraces(matchingSessionUUID)
|
||||
<< "does not have an allowable role. Refusing connection.";
|
||||
} else {
|
||||
qDebug() << "User" << profileUsername << "with session UUID"
|
||||
<< uuidStringWithoutCurlyBraces(matchingSessionUUID)
|
||||
<< "has an allowable role. Can connect.";
|
||||
}
|
||||
|
||||
// insert this UUID and a flag that indicates if they are allowed to connect
|
||||
_sessionAuthenticationHash.insert(matchingSessionUUID, connectableUsername);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const QString DS_SETTINGS_SESSIONS_GROUP = "web-sessions";
|
||||
|
||||
Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) {
|
||||
|
|
|
@ -52,6 +52,7 @@ public slots:
|
|||
/// Called by NodeList to inform us a node has been killed
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
void publicKeyJSONCallback(QNetworkReply& requestReply);
|
||||
void transactionJSONCallback(const QJsonObject& data);
|
||||
|
||||
void restart();
|
||||
|
@ -82,8 +83,17 @@ private:
|
|||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
||||
void preloadAllowedUserPublicKeys();
|
||||
void requestUserPublicKey(const QString& username);
|
||||
|
||||
int parseNodeDataFromByteArray(QDataStream& packetStream,
|
||||
NodeType_t& nodeType,
|
||||
HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||
const NodeSet& nodeInterestList);
|
||||
|
@ -131,13 +141,11 @@ private:
|
|||
QString _oauthClientID;
|
||||
QString _oauthClientSecret;
|
||||
QString _hostname;
|
||||
QMap<QNetworkReply*, QUuid> _networkReplyUUIDMap;
|
||||
QHash<QUuid, QString> _sessionAuthenticationHash;
|
||||
|
||||
QSet<QUuid> _webAuthenticationStateSet;
|
||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||
|
||||
HifiSockAddr _localSockAddr;
|
||||
QHash<QString, QByteArray> _userPublicKeys;
|
||||
|
||||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||
|
|
|
@ -1312,7 +1312,6 @@ var toolBar = (function () {
|
|||
if (clickedOverlay === loadFileMenuItem) {
|
||||
toggleNewModelButton(false);
|
||||
|
||||
// TODO BUG: this is bug, if the user has never uploaded a model, this will throw an JS exception
|
||||
file = Window.browse("Select your model file ...",
|
||||
Settings.getValue("LastModelUploadLocation").path(),
|
||||
"Model files (*.fst *.fbx)");
|
||||
|
|
246
examples/libraries/entityCameraTool.js
Normal file
246
examples/libraries/entityCameraTool.js
Normal file
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
// entityCameraTool.js
|
||||
// examples
|
||||
//
|
||||
// Created by Ryan Huffman on 10/14/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var MOUSE_SENSITIVITY = 0.9;
|
||||
var SCROLL_SENSITIVITY = 0.05;
|
||||
var PAN_ZOOM_SCALE_RATIO = 0.4;
|
||||
|
||||
// Scaling applied based on the size of the object being focused
|
||||
var FOCUS_ZOOM_SCALE = 1.3;
|
||||
|
||||
// Minimum zoom level when focusing on an object
|
||||
var FOCUS_MIN_ZOOM = 0.5;
|
||||
|
||||
// Scaling applied based on the current zoom level
|
||||
var ZOOM_SCALING = 0.02;
|
||||
|
||||
var MIN_ZOOM_DISTANCE = 0.01;
|
||||
var MAX_ZOOM_DISTANCE = 200;
|
||||
|
||||
var MODE_INACTIVE = null;
|
||||
var MODE_ORBIT = 'orbit';
|
||||
var MODE_PAN = 'pan';
|
||||
|
||||
var EASING_MULTIPLIER = 8;
|
||||
|
||||
var INITIAL_ZOOM_DISTANCE = 2;
|
||||
var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3;
|
||||
|
||||
EntityCameraTool = function() {
|
||||
var that = {};
|
||||
|
||||
that.enabled = false;
|
||||
that.mode = MODE_INACTIVE;
|
||||
|
||||
that.zoomDistance = INITIAL_ZOOM_DISTANCE;
|
||||
that.targetZoomDistance = INITIAL_ZOOM_DISTANCE;
|
||||
|
||||
that.yaw = 0;
|
||||
that.pitch = 0;
|
||||
that.targetYaw = 0;
|
||||
that.targetPitch = 0;
|
||||
|
||||
that.focalPoint = { x: 0, y: 0, z: 0 };
|
||||
that.targetFocalPoint = { x: 0, y: 0, z: 0 };
|
||||
|
||||
that.previousCameraMode = null;
|
||||
|
||||
that.lastMousePosition = { x: 0, y: 0 };
|
||||
|
||||
that.enable = function() {
|
||||
if (that.enabled) return;
|
||||
that.enabled = true;
|
||||
that.mode = MODE_INACTIVE;
|
||||
|
||||
// Pick a point INITIAL_ZOOM_DISTANCE in front of the camera to use as a focal point
|
||||
that.zoomDistance = INITIAL_ZOOM_DISTANCE;
|
||||
that.targetZoomDistance = that.zoomDistance;
|
||||
var focalPoint = Vec3.sum(Camera.getPosition(),
|
||||
Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
if (Camera.getMode() == 'first person') {
|
||||
that.targetZoomDistance = INITIAL_ZOOM_DISTANCE_FIRST_PERSON;
|
||||
}
|
||||
|
||||
// Determine the correct yaw and pitch to keep the camera in the same location
|
||||
var dPos = Vec3.subtract(focalPoint, Camera.getPosition());
|
||||
var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);
|
||||
|
||||
that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI;
|
||||
that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI;
|
||||
that.pitch = that.targetPitch;
|
||||
that.yaw = that.targetYaw;
|
||||
|
||||
that.focalPoint = focalPoint;
|
||||
that.setFocalPoint(focalPoint);
|
||||
that.previousCameraMode = Camera.getMode();
|
||||
Camera.setMode("independent");
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.disable = function() {
|
||||
if (!that.enabled) return;
|
||||
that.enabled = false;
|
||||
that.mode = MODE_INACTIVE;
|
||||
|
||||
Camera.setMode(that.previousCameraMode);
|
||||
}
|
||||
|
||||
that.focus = function(entityProperties) {
|
||||
var dim = entityProperties.dimensions;
|
||||
var size = Math.max(dim.x, Math.max(dim.y, dim.z));
|
||||
|
||||
that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM);
|
||||
|
||||
that.setFocalPoint(entityProperties.position);
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.moveFocalPoint = function(dPos) {
|
||||
that.setFocalPoint(Vec3.sum(that.focalPoint, dPos));
|
||||
}
|
||||
|
||||
that.setFocalPoint = function(pos) {
|
||||
that.targetFocalPoint = pos
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.mouseMoveEvent = function(event) {
|
||||
if (that.enabled && that.mode != MODE_INACTIVE) {
|
||||
if (that.mode == MODE_ORBIT) {
|
||||
var diffX = event.x - that.lastMousePosition.x;
|
||||
var diffY = event.y - that.lastMousePosition.y;
|
||||
that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0)
|
||||
that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0)
|
||||
|
||||
while (that.targetYaw > 180.0) that.targetYaw -= 360;
|
||||
while (that.targetYaw < -180.0) that.targetYaw += 360;
|
||||
|
||||
if (that.targetPitch > 90) that.targetPitch = 90;
|
||||
if (that.targetPitch < -90) that.targetPitch = -90;
|
||||
|
||||
that.updateCamera();
|
||||
} else if (that.mode == MODE_PAN) {
|
||||
var diffX = event.x - that.lastMousePosition.x;
|
||||
var diffY = event.y - that.lastMousePosition.y;
|
||||
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
|
||||
up = Vec3.multiply(up, diffY * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);
|
||||
right = Vec3.multiply(right, -diffX * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);
|
||||
|
||||
var dPosition = Vec3.sum(up, right);
|
||||
|
||||
that.moveFocalPoint(dPosition);
|
||||
}
|
||||
that.lastMousePosition.x = event.x;
|
||||
that.lastMousePosition.y = event.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
that.mousePressEvent = function(event) {
|
||||
if (!that.enabled) return;
|
||||
|
||||
if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) {
|
||||
that.mode = MODE_ORBIT;
|
||||
that.lastMousePosition.x = event.x;
|
||||
that.lastMousePosition.y = event.y;
|
||||
return true;
|
||||
} else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) {
|
||||
that.mode = MODE_PAN;
|
||||
that.lastMousePosition.x = event.x;
|
||||
that.lastMousePosition.y = event.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
that.mouseReleaseEvent = function(event) {
|
||||
if (!that.enabled) return;
|
||||
|
||||
that.mode = MODE_INACTIVE;
|
||||
}
|
||||
|
||||
that.wheelEvent = function(event) {
|
||||
if (!that.enabled) return;
|
||||
|
||||
var dZoom = -event.delta * SCROLL_SENSITIVITY;
|
||||
|
||||
// Scale based on current zoom level
|
||||
dZoom *= that.targetZoomDistance * ZOOM_SCALING;
|
||||
|
||||
that.targetZoomDistance = Math.max(Math.min(that.targetZoomDistance + dZoom, MAX_ZOOM_DISTANCE), MIN_ZOOM_DISTANCE);
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.updateCamera = function() {
|
||||
if (!that.enabled) return;
|
||||
|
||||
var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 });
|
||||
var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 });
|
||||
var q = Quat.multiply(yRot, xRot);
|
||||
|
||||
var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance);
|
||||
Camera.setPosition(Vec3.sum(that.focalPoint, pos));
|
||||
|
||||
yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 });
|
||||
xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 });
|
||||
q = Quat.multiply(yRot, xRot);
|
||||
|
||||
Camera.setOrientation(q);
|
||||
}
|
||||
|
||||
function normalizeDegrees(degrees) {
|
||||
while (degrees > 180) degrees -= 360;
|
||||
while (degrees < -180) degrees += 360;
|
||||
return degrees;
|
||||
}
|
||||
|
||||
// Ease the position and orbit of the camera
|
||||
that.update = function(dt) {
|
||||
var scale = Math.min(dt * EASING_MULTIPLIER, 1.0);
|
||||
|
||||
var dYaw = that.targetYaw - that.yaw;
|
||||
if (dYaw > 180) dYaw -= 360;
|
||||
if (dYaw < -180) dYaw += 360;
|
||||
|
||||
var dPitch = that.targetPitch - that.pitch;
|
||||
|
||||
that.yaw += scale * dYaw;
|
||||
that.pitch += scale * dPitch;
|
||||
|
||||
// Normalize between [-180, 180]
|
||||
that.yaw = normalizeDegrees(that.yaw);
|
||||
that.pitch = normalizeDegrees(that.pitch);
|
||||
|
||||
var dFocal = Vec3.subtract(that.targetFocalPoint, that.focalPoint);
|
||||
that.focalPoint = Vec3.sum(that.focalPoint, Vec3.multiply(scale, dFocal));
|
||||
|
||||
var dZoom = that.targetZoomDistance - that.zoomDistance;
|
||||
that.zoomDistance += scale * dZoom;
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
Script.update.connect(that.update);
|
||||
|
||||
Controller.wheelEvent.connect(that.wheelEvent);
|
||||
|
||||
return that;
|
||||
}
|
|
@ -17,6 +17,8 @@ SelectionDisplay = (function () {
|
|||
var that = {};
|
||||
|
||||
var MINIMUM_DIMENSION = 0.001;
|
||||
|
||||
var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.015;
|
||||
|
||||
var mode = "UNKNOWN";
|
||||
var overlayNames = new Array();
|
||||
|
@ -60,9 +62,17 @@ SelectionDisplay = (function () {
|
|||
var rotateHandleColor = { red: 0, green: 0, blue: 0 };
|
||||
var rotateHandleAlpha = 0.7;
|
||||
|
||||
var highlightedHandleColor = { red: 255, green: 0, blue: 0 };
|
||||
var highlightedHandleAlpha = 0.7;
|
||||
|
||||
var previousHandle = false;
|
||||
var previousHandleColor;
|
||||
var previousHandleAlpha;
|
||||
|
||||
var grabberSizeCorner = 0.025;
|
||||
var grabberSizeEdge = 0.015;
|
||||
var grabberSizeFace = 0.025;
|
||||
var grabberAlpha = 1;
|
||||
var grabberColorCorner = { red: 120, green: 120, blue: 120 };
|
||||
var grabberColorEdge = { red: 0, green: 0, blue: 0 };
|
||||
var grabberColorFace = { red: 120, green: 120, blue: 120 };
|
||||
|
@ -166,6 +176,34 @@ SelectionDisplay = (function () {
|
|||
var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge);
|
||||
var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge);
|
||||
|
||||
var cornerEdgeFaceGrabbers = [
|
||||
grabberLBN,
|
||||
grabberRBN,
|
||||
grabberLBF,
|
||||
grabberRBF,
|
||||
grabberLTN,
|
||||
grabberRTN,
|
||||
grabberLTF,
|
||||
grabberRTF,
|
||||
grabberTOP,
|
||||
grabberBOTTOM,
|
||||
grabberLEFT,
|
||||
grabberRIGHT,
|
||||
grabberNEAR,
|
||||
grabberFAR,
|
||||
grabberEdgeTR,
|
||||
grabberEdgeTL,
|
||||
grabberEdgeTF,
|
||||
grabberEdgeTN,
|
||||
grabberEdgeBR,
|
||||
grabberEdgeBL,
|
||||
grabberEdgeBF,
|
||||
grabberEdgeBN,
|
||||
grabberEdgeNR,
|
||||
grabberEdgeNL,
|
||||
grabberEdgeFR,
|
||||
grabberEdgeFL,
|
||||
];
|
||||
|
||||
var baseOverlayAngles = { x: 0, y: 0, z: 0 };
|
||||
var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles);
|
||||
|
@ -2320,12 +2358,127 @@ SelectionDisplay = (function () {
|
|||
that.stretchLEFT(event);
|
||||
break;
|
||||
default:
|
||||
// nothing to do by default
|
||||
// if not in any specific mode, then just look for handles to highlight...
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
var pickedColor;
|
||||
var pickedAlpha;
|
||||
var highlightNeeded = false;
|
||||
|
||||
if (result.intersects) {
|
||||
switch(result.overlayID) {
|
||||
case yawHandle:
|
||||
case pitchHandle:
|
||||
case rollHandle:
|
||||
pickedColor = rotateHandleColor;
|
||||
pickedAlpha = rotateHandleAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberMoveUp:
|
||||
pickedColor = rotateHandleColor;
|
||||
pickedAlpha = rotateHandleAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberLBN:
|
||||
case grabberLBF:
|
||||
case grabberRBN:
|
||||
case grabberRBF:
|
||||
case grabberLTN:
|
||||
case grabberLTF:
|
||||
case grabberRTN:
|
||||
case grabberRTF:
|
||||
pickedColor = grabberColorCorner;
|
||||
pickedAlpha = grabberAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberTOP:
|
||||
case grabberBOTTOM:
|
||||
case grabberLEFT:
|
||||
case grabberRIGHT:
|
||||
case grabberNEAR:
|
||||
case grabberFAR:
|
||||
pickedColor = grabberColorFace;
|
||||
pickedAlpha = grabberAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberEdgeTR:
|
||||
case grabberEdgeTL:
|
||||
case grabberEdgeTF:
|
||||
case grabberEdgeTN:
|
||||
case grabberEdgeBR:
|
||||
case grabberEdgeBL:
|
||||
case grabberEdgeBF:
|
||||
case grabberEdgeBN:
|
||||
case grabberEdgeNR:
|
||||
case grabberEdgeNL:
|
||||
case grabberEdgeFR:
|
||||
case grabberEdgeFL:
|
||||
pickedColor = grabberColorEdge;
|
||||
pickedAlpha = grabberAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (previousHandle) {
|
||||
Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha });
|
||||
previousHandle = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (highlightNeeded) {
|
||||
if (previousHandle) {
|
||||
Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha });
|
||||
previousHandle = false;
|
||||
}
|
||||
Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha });
|
||||
previousHandle = result.overlayID;
|
||||
previousHandleColor = pickedColor;
|
||||
previousHandleAlpha = pickedAlpha;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (previousHandle) {
|
||||
Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha });
|
||||
previousHandle = false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
that.updateHandleSizes = function() {
|
||||
if (selectedEntityProperties) {
|
||||
var diff = Vec3.subtract(selectedEntityProperties.position, Camera.getPosition());
|
||||
var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO;
|
||||
for (var i = 0; i < cornerEdgeFaceGrabbers.length; i++) {
|
||||
Overlays.editOverlay(cornerEdgeFaceGrabbers[i], {
|
||||
size: grabberSize,
|
||||
});
|
||||
}
|
||||
var handleSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 5;
|
||||
Overlays.editOverlay(yawHandle, {
|
||||
scale: handleSize,
|
||||
});
|
||||
Overlays.editOverlay(pitchHandle, {
|
||||
scale: handleSize,
|
||||
});
|
||||
Overlays.editOverlay(rollHandle, {
|
||||
scale: handleSize,
|
||||
});
|
||||
Overlays.editOverlay(grabberMoveUp, {
|
||||
scale: handleSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
Script.update.connect(that.updateHandleSizes);
|
||||
|
||||
that.mouseReleaseEvent = function(event) {
|
||||
var showHandles = false;
|
||||
// hide our rotation overlays..., and show our handles
|
||||
|
|
41
examples/metavoxels.js
Normal file
41
examples/metavoxels.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// metavoxels.js
|
||||
// examples
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.setInterval(function() {
|
||||
var spanner;
|
||||
if (Math.random() < 0.5) {
|
||||
spanner = new Sphere();
|
||||
} else {
|
||||
spanner = new Cuboid();
|
||||
spanner.aspectX = 0.1 + Math.random() * 1.9;
|
||||
spanner.aspectY = 0.1 + Math.random() * 1.9;
|
||||
}
|
||||
spanner.scale = 0.1 + Math.random() * 1.9;
|
||||
spanner.rotation = Quat.fromPitchYawRollDegrees(Math.random() * 360.0, Math.random() * 360.0, Math.random() * 360.0);
|
||||
spanner.translation = { x: 10.0 + Math.random() * 10.0, y: 10.0 + Math.random() * 10.0, z: 10.0 + Math.random() * 10.0 };
|
||||
|
||||
if (Math.random() < 0.5) {
|
||||
var material = new MaterialObject();
|
||||
if (Math.random() < 0.5) {
|
||||
material.diffuse = "http://www.fungibleinsight.com/faces/grass.jpg";
|
||||
} else {
|
||||
material.diffuse = "http://www.fungibleinsight.com/faces/soil.jpg";
|
||||
}
|
||||
Metavoxels.setVoxelMaterial(spanner, material);
|
||||
|
||||
} else if (Math.random() < 0.5) {
|
||||
Metavoxels.setVoxelColor(spanner, { red: Math.random() * 255.0, green: Math.random() * 255.0,
|
||||
blue: Math.random() * 255.0 });
|
||||
|
||||
} else {
|
||||
Metavoxels.setVoxelColor(spanner, { red: 0, green: 0, blue: 0, alpha: 0 });
|
||||
}
|
||||
}, 1000);
|
||||
|
|
@ -31,6 +31,9 @@ Script.include("libraries/ToolTip.js");
|
|||
Script.include("libraries/entityPropertyDialogBox.js");
|
||||
var entityPropertyDialogBox = EntityPropertyDialogBox;
|
||||
|
||||
Script.include("libraries/entityCameraTool.js");
|
||||
var entityCameraTool = new EntityCameraTool();
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
var toolHeight = 50;
|
||||
|
@ -163,6 +166,9 @@ var toolBar = (function () {
|
|||
Overlays.editOverlay(loadFileMenuItem, { visible: active });
|
||||
}
|
||||
|
||||
var RESIZE_INTERVAL = 50;
|
||||
var RESIZE_TIMEOUT = 20000;
|
||||
var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL;
|
||||
function addModel(url) {
|
||||
var position;
|
||||
|
||||
|
@ -176,6 +182,27 @@ var toolBar = (function () {
|
|||
modelURL: url
|
||||
});
|
||||
print("Model added: " + url);
|
||||
|
||||
var checkCount = 0;
|
||||
function resize() {
|
||||
var entityProperties = Entities.getEntityProperties(entityId);
|
||||
var naturalDimensions = entityProperties.naturalDimensions;
|
||||
|
||||
checkCount++;
|
||||
|
||||
if (naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) {
|
||||
if (checkCount < RESIZE_MAX_CHECKS) {
|
||||
Script.setTimeout(resize, RESIZE_INTERVAL);
|
||||
} else {
|
||||
print("Resize failed: timed out waiting for model (" + url + ") to load");
|
||||
}
|
||||
} else {
|
||||
entityProperties.dimensions = naturalDimensions;
|
||||
Entities.editEntity(entityId, entityProperties);
|
||||
}
|
||||
}
|
||||
|
||||
Script.setTimeout(resize, RESIZE_INTERVAL);
|
||||
} else {
|
||||
print("Can't add model: Model would be out of bounds.");
|
||||
}
|
||||
|
@ -217,6 +244,9 @@ var toolBar = (function () {
|
|||
isActive = !isActive;
|
||||
if (!isActive) {
|
||||
selectionDisplay.unselectAll();
|
||||
entityCameraTool.disable();
|
||||
} else {
|
||||
entityCameraTool.enable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -238,7 +268,6 @@ var toolBar = (function () {
|
|||
if (clickedOverlay === loadFileMenuItem) {
|
||||
toggleNewModelButton(false);
|
||||
|
||||
// TODO BUG: this is bug, if the user has never uploaded a model, this will throw an JS exception
|
||||
file = Window.browse("Select your model file ...",
|
||||
Settings.getValue("LastModelUploadLocation").path(),
|
||||
"Model files (*.fst *.fbx)");
|
||||
|
@ -293,6 +322,7 @@ var toolBar = (function () {
|
|||
}
|
||||
|
||||
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -340,7 +370,8 @@ function mousePressEvent(event) {
|
|||
mouseLastPosition = { x: event.x, y: event.y };
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)
|
||||
|| entityCameraTool.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||
// Event handled; do nothing.
|
||||
return;
|
||||
} else {
|
||||
|
@ -443,8 +474,8 @@ function mouseMoveEvent(event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// allow the selectionDisplay to handle the event first, if it doesn't handle it, then do our own thing
|
||||
if (selectionDisplay.mouseMoveEvent(event)) {
|
||||
// allow the selectionDisplay and entityCameraTool to handle the event first, if it doesn't handle it, then do our own thing
|
||||
if (selectionDisplay.mouseMoveEvent(event) || entityCameraTool.mouseMoveEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -483,6 +514,7 @@ function mouseReleaseEvent(event) {
|
|||
if (entitySelected) {
|
||||
tooltip.show(false);
|
||||
}
|
||||
entityCameraTool.mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
@ -614,6 +646,10 @@ Controller.keyReleaseEvent.connect(function (event) {
|
|||
}
|
||||
if (event.text == "BACKSPACE") {
|
||||
handeMenuEvent("Delete");
|
||||
} else if (event.text == "f") {
|
||||
if (entitySelected) {
|
||||
entityCameraTool.focus(selectedEntityProperties);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
101
hifi.podspec
101
hifi.podspec
|
@ -1,101 +0,0 @@
|
|||
#
|
||||
# Be sure to run `pod spec lint hifi.podspec' to ensure this is a
|
||||
# valid spec and remove all comments before submitting the spec.
|
||||
#
|
||||
# To learn more about the attributes see http://docs.cocoapods.org/specification.html
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "hifi"
|
||||
s.version = "0.0.1"
|
||||
s.summary = "Test platform for various render and interface tests for next-gen VR system."
|
||||
|
||||
s.homepage = "https://github.com/worklist/hifi"
|
||||
|
||||
# Specify the license type. CocoaPods detects automatically the license file if it is named
|
||||
# 'LICENCE*.*' or 'LICENSE*.*', however if the name is different, specify it.
|
||||
# s.license = 'MIT (example)'
|
||||
|
||||
# Specify the authors of the library, with email addresses. You can often find
|
||||
# the email addresses of the authors by using the SCM log. E.g. $ git log
|
||||
#
|
||||
s.author = { "Worklist" => "contact@worklist.net" }
|
||||
|
||||
# Specify the location from where the source should be retrieved.
|
||||
#
|
||||
s.source = { :git => "https://github.com/worklist/hifi.git" }
|
||||
|
||||
s.platform = :ios
|
||||
s.ios.deployment_target = "6.0"
|
||||
|
||||
# A list of file patterns which select the source files that should be
|
||||
# added to the Pods project. If the pattern is a directory then the
|
||||
# path will automatically have '*.{h,m,mm,c,cpp}' appended.
|
||||
#
|
||||
# s.source_files = 'Classes', 'Classes/**/*.{h,m}'
|
||||
# s.exclude_files = 'Classes/Exclude'
|
||||
|
||||
s.subspec "shared" do |sp|
|
||||
sp.source_files = 'libraries/shared/src', 'libraries/shared/moc_*'
|
||||
sp.exclude_files = "libraries/shared/src/UrlReader.*"
|
||||
sp.dependency 'glm'
|
||||
sp.xcconfig = { 'CLANG_CXX_LIBRARY' => "libc++" }
|
||||
end
|
||||
|
||||
s.subspec "audio" do |sp|
|
||||
sp.source_files = "libraries/audio/src"
|
||||
sp.dependency 'glm'
|
||||
end
|
||||
|
||||
s.subspec "avatars" do |sp|
|
||||
sp.source_files = 'libraries/avatars/src', 'libraries/avatars/moc_*'
|
||||
sp.dependency 'glm'
|
||||
end
|
||||
|
||||
s.subspec "voxels" do |sp|
|
||||
sp.source_files = 'libraries/voxels/src', 'libraries/voxels/moc_*'
|
||||
sp.dependency 'glm'
|
||||
end
|
||||
|
||||
s.xcconfig = { 'HEADER_SEARCH_PATHS' => '${PODS_ROOT}/../../qt5-device/qtbase/include' }
|
||||
s.libraries = 'libQtCoreCombined', 'libQt5Network', 'libQt5Script'
|
||||
|
||||
# A list of file patterns which select the header files that should be
|
||||
# made available to the application. If the pattern is a directory then the
|
||||
# path will automatically have '*.h' appended.
|
||||
#
|
||||
# If you do not explicitly set the list of public header files,
|
||||
# all headers of source_files will be made public.
|
||||
#
|
||||
# s.public_header_files = 'Classes/**/*.h'
|
||||
|
||||
# A list of paths to preserve after installing the Pod.
|
||||
# CocoaPods cleans by default any file that is not used.
|
||||
# Please don't include documentation, example, and test files.
|
||||
#
|
||||
# s.preserve_paths = "FilesToSave", "MoreFilesToSave"
|
||||
|
||||
# Specify a list of frameworks that the application needs to link
|
||||
# against for this Pod to work.
|
||||
#
|
||||
# s.framework = 'SomeFramework'
|
||||
# s.frameworks = 'SomeFramework', 'AnotherFramework'
|
||||
|
||||
# Specify a list of libraries that the application needs to link
|
||||
# against for this Pod to work.
|
||||
#
|
||||
# s.library = 'iconv'
|
||||
# s.libraries = 'iconv', 'xml2'
|
||||
|
||||
# If this Pod uses ARC, specify it like so.
|
||||
#
|
||||
s.requires_arc = false
|
||||
|
||||
# If you need to specify any other build settings, add them to the
|
||||
# xcconfig hash.
|
||||
#
|
||||
# s.xcconfig = { 'CLANG_CXX_LIBRARY' => "libc++" }
|
||||
|
||||
# Finally, specify any Pods that this Pod depends on.
|
||||
#
|
||||
# s.dependency 'JSONKit', '~> 1.4'
|
||||
end
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME interface)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2")
|
||||
set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2")
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
|
||||
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
|
||||
|
@ -105,7 +105,6 @@ link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avat
|
|||
|
||||
# find any optional and required libraries
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
# perform standard include and linking for found externals
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
|
@ -169,10 +168,9 @@ endif ()
|
|||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes")
|
||||
include_directories("${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME} ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES}
|
||||
${TARGET_NAME} ${ZLIB_LIBRARIES}
|
||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets
|
||||
)
|
||||
|
||||
|
|
11
interface/external/faceplus/readme.txt
vendored
11
interface/external/faceplus/readme.txt
vendored
|
@ -1,11 +0,0 @@
|
|||
|
||||
Instructions for adding the Faceplus driver to Interface
|
||||
Andrzej Kapolka, April 8, 2014
|
||||
|
||||
1. Copy the Faceplus sdk folders (include, win32) into the interface/external/faceplus folder.
|
||||
This readme.txt should be there as well.
|
||||
|
||||
2. Copy the Faceplus DLLs from the win32 folder into your path.
|
||||
|
||||
3. Delete your build directory, run cmake and build, and you should be all set.
|
||||
|
|
@ -71,7 +71,6 @@
|
|||
#include "InterfaceVersion.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelUploader.h"
|
||||
#include "PaymentManager.h"
|
||||
#include "Util.h"
|
||||
#include "devices/MIDIManager.h"
|
||||
#include "devices/OculusManager.h"
|
||||
|
@ -89,7 +88,6 @@
|
|||
#include "scripting/WindowScriptingInterface.h"
|
||||
|
||||
#include "ui/InfoView.h"
|
||||
#include "ui/OAuthWebViewHandler.h"
|
||||
#include "ui/Snapshot.h"
|
||||
#include "ui/Stats.h"
|
||||
#include "ui/TextRenderer.h"
|
||||
|
@ -178,7 +176,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_runningScriptsWidgetWasVisible(false),
|
||||
_trayIcon(new QSystemTrayIcon(_window)),
|
||||
_lastNackTime(usecTimestampNow()),
|
||||
_lastSendDownstreamAudioStats(usecTimestampNow())
|
||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||
_renderResolutionScale(1.0f)
|
||||
{
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
@ -186,8 +185,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// set the associated application properties
|
||||
applicationInfo.beginGroup("INFO");
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
setApplicationName(applicationInfo.value("name").toString());
|
||||
setApplicationVersion(BUILD_VERSION);
|
||||
setOrganizationName(applicationInfo.value("organizationName").toString());
|
||||
|
@ -206,6 +203,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
|
@ -218,10 +217,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
listenPort = atoi(portStr);
|
||||
}
|
||||
|
||||
// call the OAuthWebviewHandler static getter so that its instance lives in our thread
|
||||
// make sure it is ready before the NodeList might need it
|
||||
OAuthWebViewHandler::getInstance();
|
||||
|
||||
// start the nodeThread so its event loop is running
|
||||
_nodeThread->start();
|
||||
|
||||
|
@ -254,11 +249,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
|
||||
connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag);
|
||||
|
||||
// hookup VoxelEditSender to PaymentManager so we can pay for octree edits
|
||||
const PaymentManager& paymentManager = PaymentManager::getInstance();
|
||||
connect(&_voxelEditSender, &VoxelEditPacketSender::octreePaymentRequired,
|
||||
&paymentManager, &PaymentManager::sendSignedPayment);
|
||||
|
||||
// update our location every 5 seconds in the data-server, assuming that we are authenticated with one
|
||||
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
|
||||
|
||||
|
@ -589,7 +579,8 @@ void Application::paintGL() {
|
|||
if (OculusManager::isConnected()) {
|
||||
_textureCache.setFrameBufferSize(OculusManager::getRenderTargetSize());
|
||||
} else {
|
||||
_textureCache.setFrameBufferSize(_glWidget->getDeviceSize());
|
||||
QSize fbSize = _glWidget->getDeviceSize() * _renderResolutionScale;
|
||||
_textureCache.setFrameBufferSize(fbSize);
|
||||
}
|
||||
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
@ -687,6 +678,10 @@ void Application::paintGL() {
|
|||
} else {
|
||||
_glowEffect.prepare();
|
||||
|
||||
// Viewport is assigned to the size of the framebuffer
|
||||
QSize size = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->size();
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
@ -1541,9 +1536,8 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox
|
|||
|
||||
FaceTracker* Application::getActiveFaceTracker() {
|
||||
return (_dde.isActive() ? static_cast<FaceTracker*>(&_dde) :
|
||||
(_faceshift.isActive() ? static_cast<FaceTracker*>(&_faceshift) :
|
||||
(_faceplus.isActive() ? static_cast<FaceTracker*>(&_faceplus) :
|
||||
(_visage.isActive() ? static_cast<FaceTracker*>(&_visage) : NULL))));
|
||||
(_faceshift.isActive() ? static_cast<FaceTracker*>(&_faceshift) :
|
||||
(_visage.isActive() ? static_cast<FaceTracker*>(&_visage) : NULL)));
|
||||
}
|
||||
|
||||
struct SendVoxelsOperationArgs {
|
||||
|
@ -1831,7 +1825,6 @@ void Application::init() {
|
|||
|
||||
// initialize our face trackers after loading the menu settings
|
||||
_faceshift.init();
|
||||
_faceplus.init();
|
||||
_visage.init();
|
||||
|
||||
Leapmotion::init();
|
||||
|
@ -3045,6 +3038,10 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
|
|||
}
|
||||
|
||||
void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
||||
// Grab current viewport to reset it at the end
|
||||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
bool eyeRelativeCamera = false;
|
||||
if (billboard) {
|
||||
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
|
||||
|
@ -3076,14 +3073,17 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
|
||||
// set the bounds of rear mirror view
|
||||
if (billboard) {
|
||||
glViewport(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height());
|
||||
glScissor(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height());
|
||||
QSize size = getTextureCache()->getFrameBufferSize();
|
||||
glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height());
|
||||
glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height());
|
||||
} else {
|
||||
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
|
||||
QSize size = getTextureCache()->getFrameBufferSize();
|
||||
float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio();
|
||||
ratio = size.height() / (float)_glWidget->getDeviceHeight();
|
||||
int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio;
|
||||
glViewport(x, _glWidget->getDeviceHeight() - y - height, width, height);
|
||||
glScissor(x, _glWidget->getDeviceHeight() - y - height, width, height);
|
||||
glViewport(x, size.height() - y - height, width, height);
|
||||
glScissor(x, size.height() - y - height, width, height);
|
||||
}
|
||||
bool updateViewFrustum = false;
|
||||
updateProjectionMatrix(_mirrorCamera, updateViewFrustum);
|
||||
|
@ -3151,7 +3151,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
}
|
||||
|
||||
// reset Viewport and projection matrix
|
||||
glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight());
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
updateProjectionMatrix(_myCamera, updateViewFrustum);
|
||||
}
|
||||
|
@ -3336,7 +3336,6 @@ void Application::resetSensors() {
|
|||
_mouseX = _glWidget->width() / 2;
|
||||
_mouseY = _glWidget->height() / 2;
|
||||
|
||||
_faceplus.reset();
|
||||
_faceshift.reset();
|
||||
_visage.reset();
|
||||
_dde.reset();
|
||||
|
@ -3431,7 +3430,7 @@ void Application::updateLocationInServer() {
|
|||
|
||||
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
|
||||
|
||||
accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation,
|
||||
accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
|
||||
}
|
||||
}
|
||||
|
@ -3467,20 +3466,18 @@ void Application::domainChanged(const QString& domainHostname) {
|
|||
|
||||
// reset the voxels renderer
|
||||
_voxels.killLocalVoxels();
|
||||
|
||||
// reset the auth URL for OAuth web view handler
|
||||
OAuthWebViewHandler::getInstance().clearLastAuthorizationURL();
|
||||
}
|
||||
|
||||
void Application::connectedToDomain(const QString& hostname) {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
const QUuid& domainID = NodeList::getInstance()->getDomainHandler().getUUID();
|
||||
|
||||
if (accountManager.isLoggedIn() && !domainID.isNull()) {
|
||||
// update our data-server with the domain-server we're logged in with
|
||||
|
||||
if (accountManager.isLoggedIn()) {
|
||||
// update our domain-server with the data-server we're logged in with
|
||||
QString domainPutJsonString = "{\"location\":{\"domain_id\":\"" + uuidStringWithoutCurlyBraces(domainID) + "\"}}";
|
||||
|
||||
QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}";
|
||||
|
||||
accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation,
|
||||
accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), domainPutJsonString.toUtf8());
|
||||
}
|
||||
}
|
||||
|
@ -4137,3 +4134,7 @@ void Application::takeSnapshot() {
|
|||
}
|
||||
_snapshotShareDialog->show();
|
||||
}
|
||||
|
||||
void Application::setRenderResolutionScale(float scale) {
|
||||
_renderResolutionScale = scale;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "avatar/Avatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "devices/Faceplus.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/PrioVR.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
|
@ -210,7 +209,6 @@ public:
|
|||
int getMouseX() const { return _mouseX; }
|
||||
int getMouseY() const { return _mouseY; }
|
||||
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated;; }
|
||||
Faceplus* getFaceplus() { return &_faceplus; }
|
||||
Faceshift* getFaceshift() { return &_faceshift; }
|
||||
Visage* getVisage() { return &_visage; }
|
||||
DdeFaceTracker* getDDE() { return &_dde; }
|
||||
|
@ -296,6 +294,8 @@ public:
|
|||
|
||||
bool isLookingAtMyAvatar(Avatar* avatar);
|
||||
|
||||
float getRenderResolutionScale() const { return _renderResolutionScale; }
|
||||
|
||||
signals:
|
||||
|
||||
/// Fired when we're simulating; allows external parties to hook in.
|
||||
|
@ -358,6 +358,8 @@ public slots:
|
|||
|
||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||
|
||||
void setRenderResolutionScale(float scale);
|
||||
|
||||
void resetSensors();
|
||||
|
||||
private slots:
|
||||
|
@ -397,7 +399,6 @@ private:
|
|||
// Various helper functions called during update()
|
||||
void updateLOD();
|
||||
void updateMouseRay();
|
||||
void updateFaceplus();
|
||||
void updateFaceshift();
|
||||
void updateVisage();
|
||||
void updateDDE();
|
||||
|
@ -495,7 +496,6 @@ private:
|
|||
AvatarManager _avatarManager;
|
||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||
|
||||
Faceplus _faceplus;
|
||||
Faceshift _faceshift;
|
||||
Visage _visage;
|
||||
DdeFaceTracker _dde;
|
||||
|
@ -608,6 +608,8 @@ private:
|
|||
|
||||
quint64 _lastNackTime;
|
||||
quint64 _lastSendDownstreamAudioStats;
|
||||
|
||||
float _renderResolutionScale;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "AudioSourceTone.h"
|
||||
#include "AudioSourceNoise.h"
|
||||
#include "AudioGain.h"
|
||||
#include "AudioPan.h"
|
||||
#include "AudioFilter.h"
|
||||
#include "AudioFilterBank.h"
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ BuckyBalls::BuckyBalls() {
|
|||
colors[1] = glm::vec3(0.64f, 0.16f, 0.16f);
|
||||
colors[2] = glm::vec3(0.31f, 0.58f, 0.80f);
|
||||
|
||||
qDebug("Creating buckyballs...");
|
||||
for (int i = 0; i < NUM_BBALLS; i++) {
|
||||
_bballPosition[i] = CORNER_BBALLS + randVector() * RANGE_BBALLS;
|
||||
int element = (rand() % NUM_ELEMENTS);
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
|
||||
#include <QtCore/QWeakPointer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
#include "ui/OAuthWebViewHandler.h"
|
||||
|
||||
#include "DatagramProcessor.h"
|
||||
|
||||
|
@ -129,16 +129,12 @@ void DatagramProcessor::processDatagrams() {
|
|||
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size());
|
||||
break;
|
||||
}
|
||||
case PacketTypeDomainOAuthRequest: {
|
||||
QDataStream readStream(incomingPacket);
|
||||
readStream.skipRawData(numBytesForPacketHeader(incomingPacket));
|
||||
|
||||
QUrl authorizationURL;
|
||||
readStream >> authorizationURL;
|
||||
|
||||
QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL",
|
||||
Q_ARG(const QUrl&, authorizationURL));
|
||||
|
||||
case PacketTypeDomainConnectionDenied: {
|
||||
// output to the log so the user knows they got a denied connection request
|
||||
// and check and signal for an access token so that we can make sure they are logged in
|
||||
qDebug() << "The domain-server denied a connection request.";
|
||||
qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature.";
|
||||
AccountManager::getInstance().checkAndSignalForAccessToken();
|
||||
break;
|
||||
}
|
||||
case PacketTypeMuteEnvironment: {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Creates single flexible vertlet-integrated strands that can be used for hair/fur/grass
|
||||
// Creates single flexible verlet-integrated strands that can be used for hair/fur/grass
|
||||
|
||||
#include "Hair.h"
|
||||
|
||||
|
@ -17,13 +17,13 @@
|
|||
|
||||
const float HAIR_DAMPING = 0.99f;
|
||||
const float CONSTRAINT_RELAXATION = 10.0f;
|
||||
const float HAIR_ACCELERATION_COUPLING = 0.025f;
|
||||
const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.01f;
|
||||
const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.001f;
|
||||
const float HAIR_ACCELERATION_COUPLING = 0.045f;
|
||||
const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.020f;
|
||||
const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.003f;
|
||||
const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f;
|
||||
const float HAIR_STIFFNESS = 0.005f;
|
||||
const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f);
|
||||
const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f);
|
||||
const float HAIR_STIFFNESS = 0.00f;
|
||||
const glm::vec3 HAIR_COLOR1(0.98f, 0.76f, 0.075f);
|
||||
const glm::vec3 HAIR_COLOR2(0.912f, 0.184f, 0.101f);
|
||||
|
||||
Hair::Hair(int strands,
|
||||
int links,
|
||||
|
@ -38,7 +38,8 @@ Hair::Hair(int strands,
|
|||
_acceleration(0.0f),
|
||||
_angularVelocity(0.0f),
|
||||
_angularAcceleration(0.0f),
|
||||
_gravity(0.0f)
|
||||
_gravity(0.0f),
|
||||
_loudness(0.0f)
|
||||
{
|
||||
_hairPosition = new glm::vec3[_strands * _links];
|
||||
_hairOriginalPosition = new glm::vec3[_strands * _links];
|
||||
|
@ -48,12 +49,15 @@ Hair::Hair(int strands,
|
|||
_hairColors = new glm::vec3[_strands * _links];
|
||||
_hairIsMoveable = new int[_strands * _links];
|
||||
_hairConstraints = new int[_strands * _links * HAIR_CONSTRAINTS]; // Hair can link to two others
|
||||
const float FACE_WIDTH = PI / 4.0f;
|
||||
glm::vec3 thisVertex;
|
||||
for (int strand = 0; strand < _strands; strand++) {
|
||||
float strandAngle = randFloat() * PI;
|
||||
float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH));
|
||||
float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI);
|
||||
float azimuth;
|
||||
float elevation = PI_OVER_TWO - (randFloat() * 0.10f * PI);
|
||||
azimuth = PI_OVER_TWO;
|
||||
if (randFloat() < 0.5f) {
|
||||
azimuth *= -1.0f;
|
||||
}
|
||||
glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation));
|
||||
thisStrand *= _radius;
|
||||
|
||||
|
@ -77,7 +81,7 @@ Hair::Hair(int strands,
|
|||
_hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex;
|
||||
|
||||
_hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness);
|
||||
_hairQuadDelta[vertexIndex] *= 1.f - ((float)link / _links);
|
||||
_hairQuadDelta[vertexIndex] *= ((float)link / _links);
|
||||
_hairNormals[vertexIndex] = glm::normalize(randVector());
|
||||
if (randFloat() < elevation / PI_OVER_TWO) {
|
||||
_hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links);
|
||||
|
@ -86,7 +90,9 @@ Hair::Hair(int strands,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float SOUND_THRESHOLD = 50.0f;
|
||||
|
||||
void Hair::simulate(float deltaTime) {
|
||||
deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f);
|
||||
|
@ -114,9 +120,15 @@ void Hair::simulate(float deltaTime) {
|
|||
_hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) *
|
||||
(_radius - glm::length(_hairPosition[vertexIndex]));
|
||||
}
|
||||
|
||||
// Add random thing driven by loudness
|
||||
float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 8000.0f : 0.0f;
|
||||
|
||||
const float QUIESCENT_LOUDNESS = 0.0f;
|
||||
_hairPosition[vertexIndex] += randVector() * (QUIESCENT_LOUDNESS + loudnessFactor) * ((float)link / (float)_links);
|
||||
|
||||
// Add gravity
|
||||
_hairPosition[vertexIndex] += _gravity * deltaTime;
|
||||
const float SCALE_GRAVITY = 0.10f;
|
||||
_hairPosition[vertexIndex] += _gravity * deltaTime * SCALE_GRAVITY;
|
||||
|
||||
// Add linear acceleration
|
||||
_hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime;
|
||||
|
@ -168,7 +180,7 @@ void Hair::simulate(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// Store start position for next vertlet pass
|
||||
// Store start position for next verlet pass
|
||||
_hairLastPosition[vertexIndex] = thisPosition;
|
||||
}
|
||||
}
|
||||
|
@ -179,11 +191,23 @@ void Hair::render() {
|
|||
//
|
||||
// Before calling this function, translate/rotate to the origin of the owning object
|
||||
//
|
||||
float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 16.0f : 0.0f;
|
||||
const int SPARKLE_EVERY = 5;
|
||||
const float HAIR_SETBACK = 0.0f;
|
||||
int sparkleIndex = (int) (randFloat() * SPARKLE_EVERY);
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, HAIR_SETBACK);
|
||||
glBegin(GL_QUADS);
|
||||
for (int strand = 0; strand < _strands; strand++) {
|
||||
for (int link = 0; link < _links - 1; link++) {
|
||||
int vertexIndex = strand * _links + link;
|
||||
glColor3fv(&_hairColors[vertexIndex].x);
|
||||
glm::vec3 thisColor = _hairColors[vertexIndex];
|
||||
if (sparkleIndex % SPARKLE_EVERY == 0) {
|
||||
thisColor.x += (1.f - thisColor.x) * loudnessFactor;
|
||||
thisColor.y += (1.f - thisColor.y) * loudnessFactor;
|
||||
thisColor.z += (1.f - thisColor.z) * loudnessFactor;
|
||||
}
|
||||
glColor3fv(&thisColor.x);
|
||||
glNormal3fv(&_hairNormals[vertexIndex].x);
|
||||
glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y,
|
||||
|
@ -198,9 +222,11 @@ void Hair::render() {
|
|||
glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z);
|
||||
sparkleIndex++;
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
|
||||
const int HAIR_CONSTRAINTS = 2;
|
||||
|
||||
const int DEFAULT_HAIR_STRANDS = 50;
|
||||
const int DEFAULT_HAIR_STRANDS = 20;
|
||||
const int DEFAULT_HAIR_LINKS = 10;
|
||||
const float DEFAULT_HAIR_RADIUS = 0.15f;
|
||||
const float DEFAULT_HAIR_LINK_LENGTH = 0.03f;
|
||||
const float DEFAULT_HAIR_THICKNESS = 0.015f;
|
||||
const float DEFAULT_HAIR_LINK_LENGTH = 0.07f;
|
||||
const float DEFAULT_HAIR_THICKNESS = 0.025f;
|
||||
|
||||
class Hair {
|
||||
public:
|
||||
|
@ -42,6 +42,7 @@ public:
|
|||
void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; }
|
||||
void setAngularAcceleration(const glm::vec3& angularAcceleration) { _angularAcceleration = angularAcceleration; }
|
||||
void setGravity(const glm::vec3& gravity) { _gravity = gravity; }
|
||||
void setLoudness(const float loudness) { _loudness = loudness; }
|
||||
|
||||
private:
|
||||
int _strands;
|
||||
|
@ -61,7 +62,7 @@ private:
|
|||
glm::vec3 _angularVelocity;
|
||||
glm::vec3 _angularAcceleration;
|
||||
glm::vec3 _gravity;
|
||||
|
||||
float _loudness;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -122,7 +122,8 @@ Menu::Menu() :
|
|||
_hasLoginDialogDisplayed(false),
|
||||
_snapshotsLocation(),
|
||||
_scriptsLocation(),
|
||||
_walletPrivateKey()
|
||||
_walletPrivateKey(),
|
||||
_shouldRenderTableNeedsRebuilding(true)
|
||||
{
|
||||
Application *appInstance = Application::getInstance();
|
||||
|
||||
|
@ -158,7 +159,7 @@ Menu::Menu() :
|
|||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, appInstance, SLOT(loadScriptURLDialog()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::SHIFT | Qt::Key_R,
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
appInstance, SLOT(reloadAllScripts()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
appInstance, SLOT(toggleRunningScriptsWidget()));
|
||||
|
@ -246,10 +247,16 @@ Menu::Menu() :
|
|||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
this,
|
||||
SLOT(toggleConsole()));
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
this,
|
||||
SLOT(toggleConsole()));
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ResetSensors,
|
||||
Qt::Key_Apostrophe,
|
||||
appInstance,
|
||||
SLOT(resetSensors()));
|
||||
|
||||
QMenu* avatarMenu = addMenu("Avatar");
|
||||
|
||||
|
@ -365,7 +372,15 @@ Menu::Menu() :
|
|||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||
|
||||
|
||||
QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, true));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
connect(resolutionMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderResolution(QAction*)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu,
|
||||
MenuOption::Voxels,
|
||||
|
@ -374,11 +389,7 @@ Menu::Menu() :
|
|||
appInstance,
|
||||
SLOT(setRenderVoxels(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu,
|
||||
MenuOption::GlowMode,
|
||||
0,
|
||||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
|
||||
|
||||
|
@ -391,10 +402,6 @@ Menu::Menu() :
|
|||
appInstance->getFaceshift(),
|
||||
SLOT(setTCPEnabled(bool)));
|
||||
#endif
|
||||
#ifdef HAVE_FACEPLUS
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Faceplus, 0, true,
|
||||
appInstance->getFaceplus(), SLOT(updateEnabled()));
|
||||
#endif
|
||||
#ifdef HAVE_VISAGE
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Visage, 0, false,
|
||||
appInstance->getVisage(), SLOT(updateEnabled()));
|
||||
|
@ -410,6 +417,10 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelBounds, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementProxy, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
|
||||
QMenu* modelCullingMenu = modelDebugMenu->addMenu("Culling");
|
||||
addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false);
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxels");
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
|
||||
|
@ -1219,6 +1230,23 @@ void Menu::muteEnvironment() {
|
|||
free(packet);
|
||||
}
|
||||
|
||||
void Menu::changeRenderResolution(QAction* action) {
|
||||
QString text = action->text();
|
||||
if (text == MenuOption::RenderResolutionOne) {
|
||||
Application::getInstance()->setRenderResolutionScale(1.f);
|
||||
} else if (text == MenuOption::RenderResolutionTwoThird) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.666f);
|
||||
} else if (text == MenuOption::RenderResolutionHalf) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.5f);
|
||||
} else if (text == MenuOption::RenderResolutionThird) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.333f);
|
||||
} else if (text == MenuOption::RenderResolutionQuarter) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.25f);
|
||||
} else {
|
||||
Application::getInstance()->setRenderResolutionScale(1.f);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::displayNameLocationResponse(const QString& errorString) {
|
||||
|
||||
if (!errorString.isEmpty()) {
|
||||
|
@ -1517,6 +1545,7 @@ void Menu::autoAdjustLOD(float currentFPS) {
|
|||
&& _voxelSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
|
||||
_voxelSizeScale *= ADJUST_LOD_DOWN_BY;
|
||||
|
||||
if (_voxelSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_voxelSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
}
|
||||
|
@ -1539,6 +1568,7 @@ void Menu::autoAdjustLOD(float currentFPS) {
|
|||
}
|
||||
|
||||
if (changed) {
|
||||
_shouldRenderTableNeedsRebuilding = true;
|
||||
if (_lodToolsDialog) {
|
||||
_lodToolsDialog->reloadSliders();
|
||||
}
|
||||
|
@ -1553,14 +1583,56 @@ void Menu::resetLODAdjust() {
|
|||
|
||||
void Menu::setVoxelSizeScale(float sizeScale) {
|
||||
_voxelSizeScale = sizeScale;
|
||||
_shouldRenderTableNeedsRebuilding = true;
|
||||
bumpSettings();
|
||||
}
|
||||
|
||||
void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
|
||||
_boundaryLevelAdjust = boundaryLevelAdjust;
|
||||
_shouldRenderTableNeedsRebuilding = true;
|
||||
bumpSettings();
|
||||
}
|
||||
|
||||
// TODO: This is essentially the same logic used to render voxels, but since models are more detailed then voxels
|
||||
// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it.
|
||||
bool Menu::shouldRenderMesh(float largestDimension, float distanceToCamera) {
|
||||
const float voxelToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
|
||||
float voxelSizeScale = getVoxelSizeScale();
|
||||
int boundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||
float maxScale = (float)TREE_SCALE;
|
||||
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, voxelSizeScale) / voxelToMeshRatio;
|
||||
|
||||
if (_shouldRenderTableNeedsRebuilding) {
|
||||
_shouldRenderTable.clear();
|
||||
|
||||
float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small
|
||||
float scale = maxScale;
|
||||
float visibleDistanceAtScale = visibleDistanceAtMaxScale;
|
||||
|
||||
while (scale > SMALLEST_SCALE_IN_TABLE) {
|
||||
scale /= 2.0f;
|
||||
visibleDistanceAtScale /= 2.0f;
|
||||
_shouldRenderTable[scale] = visibleDistanceAtScale;
|
||||
}
|
||||
_shouldRenderTableNeedsRebuilding = false;
|
||||
}
|
||||
|
||||
float closestScale = maxScale;
|
||||
float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale;
|
||||
QMap<float, float>::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension);
|
||||
if (lowerBound != _shouldRenderTable.constEnd()) {
|
||||
closestScale = lowerBound.key();
|
||||
visibleDistanceAtClosestScale = lowerBound.value();
|
||||
}
|
||||
|
||||
if (closestScale < largestDimension) {
|
||||
visibleDistanceAtClosestScale *= 2.0f;
|
||||
}
|
||||
|
||||
return (distanceToCamera <= visibleDistanceAtClosestScale);
|
||||
}
|
||||
|
||||
|
||||
void Menu::lodTools() {
|
||||
if (!_lodToolsDialog) {
|
||||
_lodToolsDialog = new LodToolsDialog(Application::getInstance()->getGLWidget());
|
||||
|
|
|
@ -143,6 +143,8 @@ public:
|
|||
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
bool shouldRenderMesh(float largestDimension, float distanceToCamera);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
SpeechRecognizer* getSpeechRecognizer() { return &_speechRecognizer; }
|
||||
#endif
|
||||
|
@ -225,6 +227,7 @@ private slots:
|
|||
void displayAddressOfflineMessage();
|
||||
void displayAddressNotFoundMessage();
|
||||
void muteEnvironment();
|
||||
void changeRenderResolution(QAction* action);
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
|
@ -309,6 +312,9 @@ private:
|
|||
QString _snapshotsLocation;
|
||||
QString _scriptsLocation;
|
||||
QByteArray _walletPrivateKey;
|
||||
|
||||
bool _shouldRenderTableNeedsRebuilding;
|
||||
QMap<float, float> _shouldRenderTable;
|
||||
|
||||
};
|
||||
|
||||
|
@ -365,6 +371,9 @@ namespace MenuOption {
|
|||
const QString Collisions = "Collisions";
|
||||
const QString Console = "Console...";
|
||||
const QString ControlWithSpeech = "Control With Speech";
|
||||
const QString DontCullOutOfViewMeshParts = "Don't Cull Out Of View Mesh Parts";
|
||||
const QString DontCullTooSmallMeshParts = "Don't Cull Too Small Mesh Parts";
|
||||
const QString DontReduceMaterialSwitches = "Don't Attempt to Reduce Material Switches";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||
|
@ -389,7 +398,6 @@ namespace MenuOption {
|
|||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
const QString ExpandPaintGLTiming = "Expand /paintGL";
|
||||
const QString ExpandUpdateTiming = "Expand /update";
|
||||
const QString Faceplus = "Faceplus";
|
||||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString FirstPerson = "First Person";
|
||||
|
@ -397,7 +405,6 @@ namespace MenuOption {
|
|||
const QString FrustumRenderMode = "Render Mode";
|
||||
const QString Fullscreen = "Fullscreen";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString GlowMode = "Cycle Glow Mode";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString NamesAboveHeads = "Names Above Heads";
|
||||
const QString GoToUser = "Go To User";
|
||||
|
@ -439,7 +446,14 @@ namespace MenuOption {
|
|||
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
|
||||
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
||||
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
||||
const QString RenderResolution = "Scale Resolution";
|
||||
const QString RenderResolutionOne = "1";
|
||||
const QString RenderResolutionTwoThird = "2/3";
|
||||
const QString RenderResolutionHalf = "1/2";
|
||||
const QString RenderResolutionThird = "1/3";
|
||||
const QString RenderResolutionQuarter = "1/4";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include <MetavoxelMessages.h>
|
||||
#include <MetavoxelUtil.h>
|
||||
#include <ScriptCache.h>
|
||||
|
||||
|
@ -454,6 +455,36 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) {
|
|||
return visitor.height;
|
||||
}
|
||||
|
||||
void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) };
|
||||
applyEdit(edit, true);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, material)) };
|
||||
applyMaterialEdit(edit, true);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::paintVoxelColor(const glm::vec3& position, float radius, const QColor& color) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, SharedObjectPointer(), color)) };
|
||||
applyEdit(edit, true);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, material)) };
|
||||
applyMaterialEdit(edit, true);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::setVoxelColor(const SharedObjectPointer& spanner, const QColor& color) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, SharedObjectPointer(), color)) };
|
||||
applyEdit(edit, true);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material)) };
|
||||
applyMaterialEdit(edit, true);
|
||||
}
|
||||
|
||||
class CursorRenderVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
|
@ -563,6 +594,55 @@ void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) {
|
|||
glDeleteTextures(1, (GLuint*)&textureID);
|
||||
}
|
||||
|
||||
class MaterialEditApplier : public SignalHandler {
|
||||
public:
|
||||
|
||||
MaterialEditApplier(const MetavoxelEditMessage& message, const QSharedPointer<NetworkTexture> texture);
|
||||
|
||||
virtual void handle();
|
||||
|
||||
protected:
|
||||
|
||||
MetavoxelEditMessage _message;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
};
|
||||
|
||||
MaterialEditApplier::MaterialEditApplier(const MetavoxelEditMessage& message, const QSharedPointer<NetworkTexture> texture) :
|
||||
_message(message),
|
||||
_texture(texture) {
|
||||
}
|
||||
|
||||
void MaterialEditApplier::handle() {
|
||||
static_cast<MaterialEdit*>(_message.edit.data())->averageColor = _texture->getAverageColor();
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(_message, true);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void MetavoxelSystem::applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable) {
|
||||
const MaterialEdit* edit = static_cast<const MaterialEdit*>(message.edit.constData());
|
||||
MaterialObject* material = static_cast<MaterialObject*>(edit->material.data());
|
||||
if (material && material->getDiffuse().isValid()) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "applyMaterialEdit", Q_ARG(const MetavoxelEditMessage&, message),
|
||||
Q_ARG(bool, reliable));
|
||||
return;
|
||||
}
|
||||
QSharedPointer<NetworkTexture> texture = Application::getInstance()->getTextureCache()->getTexture(
|
||||
material->getDiffuse(), SPLAT_TEXTURE);
|
||||
if (texture->isLoaded()) {
|
||||
MetavoxelEditMessage newMessage = message;
|
||||
static_cast<MaterialEdit*>(newMessage.edit.data())->averageColor = texture->getAverageColor();
|
||||
applyEdit(newMessage, true);
|
||||
|
||||
} else {
|
||||
MaterialEditApplier* applier = new MaterialEditApplier(message, texture);
|
||||
connect(texture.data(), &Resource::loaded, applier, &SignalHandler::handle);
|
||||
}
|
||||
} else {
|
||||
applyEdit(message, true);
|
||||
}
|
||||
}
|
||||
|
||||
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) {
|
||||
return new MetavoxelSystemClient(node, _updater);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,18 @@ public:
|
|||
|
||||
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
|
||||
|
||||
Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color);
|
||||
|
||||
Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
|
||||
|
||||
Q_INVOKABLE void paintVoxelColor(const glm::vec3& position, float radius, const QColor& color);
|
||||
|
||||
Q_INVOKABLE void paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
|
||||
|
||||
Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color);
|
||||
|
||||
Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material);
|
||||
|
||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
|
||||
|
||||
signals:
|
||||
|
@ -66,6 +78,8 @@ public slots:
|
|||
|
||||
protected:
|
||||
|
||||
Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false);
|
||||
|
||||
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
|
||||
|
||||
private:
|
||||
|
@ -81,6 +95,15 @@ private:
|
|||
Frustum _frustum;
|
||||
};
|
||||
|
||||
/// Generic abstract base class for objects that handle a signal.
|
||||
class SignalHandler : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void handle() = 0;
|
||||
};
|
||||
|
||||
/// Describes contents of a point in a point buffer.
|
||||
class BufferPoint {
|
||||
public:
|
||||
|
|
|
@ -364,7 +364,9 @@ void ModelUploader::send() {
|
|||
_progressBar = NULL;
|
||||
}
|
||||
|
||||
void ModelUploader::checkJSON(const QJsonObject& jsonResponse) {
|
||||
void ModelUploader::checkJSON(QNetworkReply& requestReply) {
|
||||
QJsonObject jsonResponse = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||
|
||||
if (jsonResponse.contains("status") && jsonResponse.value("status").toString() == "success") {
|
||||
qDebug() << "status : success";
|
||||
JSONCallbackParameters callbackParams;
|
||||
|
@ -426,7 +428,7 @@ void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) {
|
|||
}
|
||||
}
|
||||
|
||||
void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) {
|
||||
void ModelUploader::uploadSuccess(QNetworkReply& requestReply) {
|
||||
if (_progressDialog) {
|
||||
_progressDialog->accept();
|
||||
}
|
||||
|
|
|
@ -40,9 +40,9 @@ public slots:
|
|||
void send();
|
||||
|
||||
private slots:
|
||||
void checkJSON(const QJsonObject& jsonResponse);
|
||||
void checkJSON(QNetworkReply& requestReply);
|
||||
void uploadUpdate(qint64 bytesSent, qint64 bytesTotal);
|
||||
void uploadSuccess(const QJsonObject& jsonResponse);
|
||||
void uploadSuccess(QNetworkReply& requestReply);
|
||||
void uploadFailed(QNetworkReply& errorReply);
|
||||
void checkS3();
|
||||
void processCheck();
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// PaymentManager.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-07-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <Menu.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "SignedWalletTransaction.h"
|
||||
|
||||
#include "PaymentManager.h"
|
||||
|
||||
PaymentManager& PaymentManager::getInstance() {
|
||||
static PaymentManager sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) {
|
||||
|
||||
QByteArray walletPrivateKeyByteArray = Menu::getInstance()->getWalletPrivateKey();
|
||||
|
||||
if (!walletPrivateKeyByteArray.isEmpty()) {
|
||||
// setup a signed wallet transaction
|
||||
const qint64 DEFAULT_TRANSACTION_EXPIRY_SECONDS = 60;
|
||||
qint64 currentTimestamp = QDateTime::currentDateTimeUtc().toTime_t();
|
||||
SignedWalletTransaction newTransaction(destinationWalletUUID, satoshiAmount,
|
||||
currentTimestamp, DEFAULT_TRANSACTION_EXPIRY_SECONDS);
|
||||
|
||||
// send the signed transaction to the redeeming node
|
||||
QByteArray transactionByteArray = byteArrayWithPopulatedHeader(PacketTypeSignedTransactionPayment);
|
||||
|
||||
// append the binary message and the signed message digest
|
||||
transactionByteArray.append(newTransaction.binaryMessage());
|
||||
QByteArray signedMessageDigest = newTransaction.signedMessageDigest();
|
||||
|
||||
if (!signedMessageDigest.isEmpty()) {
|
||||
transactionByteArray.append(signedMessageDigest);
|
||||
|
||||
qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID;
|
||||
|
||||
// use the NodeList to send that to the right node
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->writeDatagram(transactionByteArray, nodeList->nodeWithUUID(nodeUUID));
|
||||
} else {
|
||||
qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID <<
|
||||
"cannot be sent because there was an error signing the transaction.";
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID <<
|
||||
"cannot be sent because there is no stored wallet private key.";
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// PaymentManager.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-07-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_PaymentManager_h
|
||||
#define hifi_PaymentManager_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class PaymentManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static PaymentManager& getInstance();
|
||||
public slots:
|
||||
void sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID);
|
||||
};
|
||||
|
||||
#endif // hifi_PaymentManager_h
|
|
@ -1,86 +0,0 @@
|
|||
//
|
||||
// SignedWalletTransaction.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-07-11.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
|
||||
#include "Menu.h"
|
||||
|
||||
#include "SignedWalletTransaction.h"
|
||||
|
||||
SignedWalletTransaction::SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount,
|
||||
qint64 messageTimestamp, qint64 expiryDelta) :
|
||||
WalletTransaction(destinationUUID, amount),
|
||||
_messageTimestamp(messageTimestamp),
|
||||
_expiryDelta(expiryDelta)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QByteArray SignedWalletTransaction::binaryMessage() {
|
||||
// build the message using the components of this transaction
|
||||
|
||||
// UUID, source UUID, destination UUID, message timestamp, expiry delta, amount
|
||||
QByteArray messageBinary;
|
||||
|
||||
messageBinary.append(_uuid.toRfc4122());
|
||||
|
||||
messageBinary.append(reinterpret_cast<const char*>(&_messageTimestamp), sizeof(_messageTimestamp));
|
||||
messageBinary.append(reinterpret_cast<const char*>(&_expiryDelta), sizeof(_expiryDelta));
|
||||
|
||||
messageBinary.append(AccountManager::getInstance().getAccountInfo().getWalletID().toRfc4122());
|
||||
|
||||
messageBinary.append(_destinationUUID.toRfc4122());
|
||||
|
||||
messageBinary.append(reinterpret_cast<const char*>(&_amount), sizeof(_amount));
|
||||
|
||||
return messageBinary;
|
||||
}
|
||||
|
||||
QByteArray SignedWalletTransaction::hexMessage() {
|
||||
return binaryMessage().toHex();
|
||||
}
|
||||
|
||||
QByteArray SignedWalletTransaction::messageDigest() {
|
||||
return QCryptographicHash::hash(hexMessage(), QCryptographicHash::Sha256).toHex();
|
||||
}
|
||||
|
||||
QByteArray SignedWalletTransaction::signedMessageDigest() {
|
||||
// pull the current private key from menu into RSA structure in memory
|
||||
QByteArray privateKeyByteArray = Menu::getInstance()->getWalletPrivateKey();
|
||||
|
||||
BIO* privateKeyBIO = NULL;
|
||||
RSA* rsaPrivateKey = NULL;
|
||||
|
||||
privateKeyBIO = BIO_new_mem_buf(privateKeyByteArray.data(), privateKeyByteArray.size());
|
||||
PEM_read_bio_RSAPrivateKey(privateKeyBIO, &rsaPrivateKey, NULL, NULL);
|
||||
|
||||
QByteArray digestToEncrypt = messageDigest();
|
||||
QByteArray encryptedDigest(RSA_size(rsaPrivateKey), 0);
|
||||
|
||||
int encryptReturn = RSA_private_encrypt(digestToEncrypt.size(),
|
||||
reinterpret_cast<const unsigned char*>(digestToEncrypt.constData()),
|
||||
reinterpret_cast<unsigned char*>(encryptedDigest.data()),
|
||||
rsaPrivateKey, RSA_PKCS1_PADDING);
|
||||
|
||||
// free the two structures used
|
||||
BIO_free(privateKeyBIO);
|
||||
RSA_free(rsaPrivateKey);
|
||||
|
||||
return (encryptReturn != -1) ? encryptedDigest : QByteArray();
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
//
|
||||
// SignedWalletTransaction.h
|
||||
// interfac/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-07-11.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_SignedWalletTransaction_h
|
||||
#define hifi_SignedWalletTransaction_h
|
||||
|
||||
#include <WalletTransaction.h>
|
||||
|
||||
class SignedWalletTransaction : public WalletTransaction {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, qint64 messageTimestamp, qint64 expiryDelta);
|
||||
|
||||
QByteArray binaryMessage();
|
||||
QByteArray hexMessage();
|
||||
QByteArray messageDigest();
|
||||
QByteArray signedMessageDigest();
|
||||
|
||||
private:
|
||||
qint64 _messageTimestamp;
|
||||
qint64 _expiryDelta;
|
||||
};
|
||||
|
||||
#endif // hifi_SignedWalletTransaction_h
|
|
@ -37,92 +37,7 @@ using namespace std;
|
|||
#define WORKAROUND_BROKEN_GLUT_STROKES
|
||||
// see http://www.opengl.org/resources/libraries/glut/spec3/node78.html
|
||||
|
||||
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * front, glm::vec3 * right, glm::vec3 * up) {
|
||||
//
|
||||
// Converts from three euler angles to the associated orthonormal vectors
|
||||
//
|
||||
// Angles contains (pitch, yaw, roll) in radians
|
||||
//
|
||||
|
||||
// First, create the quaternion associated with these euler angles
|
||||
glm::quat q(glm::vec3(angles->x, -(angles->y), angles->z));
|
||||
|
||||
// Next, create a rotation matrix from that quaternion
|
||||
glm::mat4 rotation;
|
||||
rotation = glm::mat4_cast(q);
|
||||
|
||||
// Transform the original vectors by the rotation matrix to get the new vectors
|
||||
glm::vec4 qup(0,1,0,0);
|
||||
glm::vec4 qright(-1,0,0,0);
|
||||
glm::vec4 qfront(0,0,1,0);
|
||||
glm::vec4 upNew = qup*rotation;
|
||||
glm::vec4 rightNew = qright*rotation;
|
||||
glm::vec4 frontNew = qfront*rotation;
|
||||
|
||||
// Copy the answers to output vectors
|
||||
up->x = upNew.x; up->y = upNew.y; up->z = upNew.z;
|
||||
right->x = rightNew.x; right->y = rightNew.y; right->z = rightNew.z;
|
||||
front->x = frontNew.x; front->y = frontNew.y; front->z = frontNew.z;
|
||||
}
|
||||
|
||||
void printVector(glm::vec3 vec) {
|
||||
qDebug("%4.2f, %4.2f, %4.2f", vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
|
||||
// Return the azimuth angle (in radians) between two points.
|
||||
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) {
|
||||
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z);
|
||||
}
|
||||
|
||||
// Return the angle (in radians) between the head and an object in the scene.
|
||||
// The value is zero if you are looking right at it.
|
||||
// The angle is negative if the object is to your right.
|
||||
float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw) {
|
||||
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) + render_yaw + head_yaw;
|
||||
}
|
||||
|
||||
// Draw a 3D vector floating in space
|
||||
void drawVector(glm::vec3 * vector) {
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glPointSize(3.0);
|
||||
glLineWidth(2.0);
|
||||
|
||||
// Draw axes
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1,0,0);
|
||||
glVertex3f(0,0,0);
|
||||
glVertex3f(1,0,0);
|
||||
glColor3f(0,1,0);
|
||||
glVertex3f(0,0,0);
|
||||
glVertex3f(0, 1, 0);
|
||||
glColor3f(0,0,1);
|
||||
glVertex3f(0,0,0);
|
||||
glVertex3f(0, 0, 1);
|
||||
glEnd();
|
||||
|
||||
// Draw the vector itself
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1,1,1);
|
||||
glVertex3f(0,0,0);
|
||||
glVertex3f(vector->x, vector->y, vector->z);
|
||||
glEnd();
|
||||
|
||||
// Draw spheres for magnitude
|
||||
glPushMatrix();
|
||||
glColor3f(1,0,0);
|
||||
glTranslatef(vector->x, 0, 0);
|
||||
Application::getInstance()->getGeometryCache()->renderSphere(0.02f, 10, 10);
|
||||
glColor3f(0,1,0);
|
||||
glTranslatef(-vector->x, vector->y, 0);
|
||||
Application::getInstance()->getGeometryCache()->renderSphere(0.02f, 10, 10);
|
||||
glColor3f(0,0,1);
|
||||
glTranslatef(0, -vector->y, vector->z);
|
||||
Application::getInstance()->getGeometryCache()->renderSphere(0.02f, 10, 10);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
void renderWorldBox() {
|
||||
// Show edge of world
|
||||
|
@ -201,10 +116,6 @@ int widthText(float scale, int mono, char const* string) {
|
|||
return textRenderer(mono)->computeWidth(string) * (scale / 0.10);
|
||||
}
|
||||
|
||||
float widthChar(float scale, int mono, char ch) {
|
||||
return textRenderer(mono)->computeWidth(ch) * (scale / 0.10);
|
||||
}
|
||||
|
||||
void drawText(int x, int y, float scale, float radians, int mono,
|
||||
char const* string, const float* color) {
|
||||
//
|
||||
|
@ -219,29 +130,6 @@ void drawText(int x, int y, float scale, float radians, int mono,
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r, float g, float b) {
|
||||
//
|
||||
// Draws vec3 on screen as stroked so it can be resized
|
||||
//
|
||||
char vectext[20];
|
||||
sprintf(vectext,"%3.1f,%3.1f,%3.1f", vec.x, vec.y, vec.z);
|
||||
int len, i;
|
||||
glPushMatrix();
|
||||
glTranslatef(static_cast<float>(x), static_cast<float>(y), 0);
|
||||
glColor3f(r,g,b);
|
||||
glRotated(180.0 + double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0);
|
||||
glRotated(180.0, 0.0, 1.0, 0.0);
|
||||
glLineWidth(thick);
|
||||
glScalef(scale, scale, 1.f);
|
||||
len = (int) strlen(vectext);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!mono) glutStrokeCharacter(GLUT_STROKE_ROMAN, int(vectext[i]));
|
||||
else glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int(vectext[i]));
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void renderCollisionOverlay(int width, int height, float magnitude, float red, float blue, float green) {
|
||||
const float MIN_VISIBLE_COLLISION = 0.01f;
|
||||
if (magnitude > MIN_VISIBLE_COLLISION) {
|
||||
|
@ -255,27 +143,6 @@ void renderCollisionOverlay(int width, int height, float magnitude, float red, f
|
|||
}
|
||||
}
|
||||
|
||||
void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition) {
|
||||
glm::vec3 vectorToPosition(glm::normalize(position - cameraPosition));
|
||||
glm::vec3 right = glm::cross(vectorToPosition, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::vec3 up = glm::cross(right, vectorToPosition);
|
||||
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (int i=0; i<numSides+1; i++) {
|
||||
float r = ((float)i / (float)numSides) * TWO_PI;
|
||||
float s = radius * sinf(r);
|
||||
float c = radius * cosf(r);
|
||||
|
||||
glVertex3f
|
||||
(
|
||||
position.x + right.x * s + up.x * c,
|
||||
position.y + right.y * s + up.y * c,
|
||||
position.z + right.z * s + up.z * c
|
||||
);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int numSides) {
|
||||
|
@ -321,54 +188,6 @@ void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistan
|
|||
glEnd();
|
||||
}
|
||||
|
||||
void renderRoundedCornersRect(int x, int y, int width, int height, int radius, int numPointsCorner) {
|
||||
#define MAX_POINTS_CORNER 50
|
||||
// At least "2" is needed
|
||||
if (numPointsCorner <= 1) {
|
||||
return;
|
||||
}
|
||||
if (numPointsCorner > MAX_POINTS_CORNER) {
|
||||
numPointsCorner = MAX_POINTS_CORNER;
|
||||
}
|
||||
|
||||
// Precompute sin and cos for [0, PI/2) for the number of points (numPointCorner)
|
||||
double radiusTimesSin[MAX_POINTS_CORNER];
|
||||
double radiusTimesCos[MAX_POINTS_CORNER];
|
||||
int i = 0;
|
||||
for (int i = 0; i < numPointsCorner; i++) {
|
||||
double t = (double)i * (double)PI_OVER_TWO / (double)(numPointsCorner - 1);
|
||||
radiusTimesSin[i] = radius * sin(t);
|
||||
radiusTimesCos[i] = radius * cos(t);
|
||||
}
|
||||
|
||||
glm::dvec2 cornerCenter;
|
||||
glBegin(GL_POINTS);
|
||||
|
||||
// Top left corner
|
||||
cornerCenter = glm::vec2(x + radius, y + height - radius);
|
||||
for (i = 0; i < numPointsCorner; i++) {
|
||||
glVertex2d(cornerCenter.x - radiusTimesCos[i], cornerCenter.y + radiusTimesSin[i]);
|
||||
}
|
||||
|
||||
// Top rigth corner
|
||||
cornerCenter = glm::vec2(x + width - radius, y + height - radius);
|
||||
for (i = 0; i < numPointsCorner; i++) {
|
||||
glVertex2d(cornerCenter.x + radiusTimesSin[i], cornerCenter.y + radiusTimesCos[i]);
|
||||
}
|
||||
|
||||
// Bottom right
|
||||
cornerCenter = glm::vec2(x + width - radius, y + radius);
|
||||
for (i = 0; i < numPointsCorner; i++) {
|
||||
glVertex2d(cornerCenter.x + radiusTimesCos[i], cornerCenter.y - radiusTimesSin[i]);
|
||||
}
|
||||
|
||||
// Bottom left
|
||||
cornerCenter = glm::vec2(x + radius, y + radius);
|
||||
for (i = 0; i < numPointsCorner; i++) {
|
||||
glVertex2d(cornerCenter.x - radiusTimesSin[i], cornerCenter.y - radiusTimesCos[i]);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
void renderOrientationDirections(glm::vec3 position, const glm::quat& orientation, float size) {
|
||||
|
@ -395,12 +214,6 @@ void renderOrientationDirections(glm::vec3 position, const glm::quat& orientatio
|
|||
glEnd();
|
||||
}
|
||||
|
||||
bool closeEnoughForGovernmentWork(float a, float b) {
|
||||
float distance = std::abs(a-b);
|
||||
//qDebug("closeEnoughForGovernmentWork() a=%1.10f b=%1.10f distance=%1.10f\n",a,b,distance);
|
||||
return (distance < 0.00001f);
|
||||
}
|
||||
|
||||
// Do some basic timing tests and report the results
|
||||
void runTimingTests() {
|
||||
// How long does it take to make a call to get the time?
|
||||
|
|
|
@ -16,35 +16,20 @@
|
|||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <QSettings>
|
||||
|
||||
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
|
||||
|
||||
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
|
||||
float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw);
|
||||
|
||||
float randFloat();
|
||||
const glm::vec3 randVector();
|
||||
|
||||
void renderWorldBox();
|
||||
int widthText(float scale, int mono, char const* string);
|
||||
float widthChar(float scale, int mono, char ch);
|
||||
|
||||
void drawText(int x, int y, float scale, float radians, int mono,
|
||||
char const* string, const float* color);
|
||||
|
||||
void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec,
|
||||
float r=1.0, float g=1.0, float b=1.0);
|
||||
|
||||
void drawVector(glm::vec3* vector);
|
||||
|
||||
void printVector(glm::vec3 vec);
|
||||
|
||||
void renderCollisionOverlay(int width, int height, float magnitude, float red = 0, float blue = 0, float green = 0);
|
||||
|
||||
void renderOrientationDirections( glm::vec3 position, const glm::quat& orientation, float size );
|
||||
|
||||
void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition);
|
||||
void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int numSides );
|
||||
void renderRoundedCornersRect(int x, int y, int width, int height, int radius, int numPointsCorner);
|
||||
|
||||
void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance);
|
||||
|
||||
void runTimingTests();
|
||||
|
|
|
@ -192,6 +192,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
_hair.setAngularVelocity((getAngularVelocity() + getHead()->getAngularVelocity()) * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setAngularAcceleration(getAngularAcceleration() * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setLoudness((float) getHeadData()->getAudioLoudness());
|
||||
_hair.simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
@ -694,7 +695,7 @@ void Avatar::renderDisplayName() {
|
|||
|
||||
if (success) {
|
||||
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() *
|
||||
float scaleFactor = Application::getInstance()->getRenderResolutionScale() *
|
||||
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
|
|
|
@ -218,6 +218,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_hair.setAngularVelocity((getAngularVelocity() + getHead()->getAngularVelocity()) * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setAngularAcceleration(getAngularAcceleration() * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setLoudness((float)getHeadData()->getAudioLoudness());
|
||||
_hair.simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
//
|
||||
// Faceplus.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/9/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#ifdef HAVE_FACEPLUS
|
||||
#include <faceplus.h>
|
||||
#endif
|
||||
|
||||
#include <FBXReader.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Faceplus.h"
|
||||
|
||||
static int floatVectorMetaTypeId = qRegisterMetaType<QVector<float> >();
|
||||
|
||||
Faceplus::Faceplus() :
|
||||
_enabled(false),
|
||||
_active(false) {
|
||||
|
||||
#ifdef HAVE_FACEPLUS
|
||||
// these are ignored--any values will do
|
||||
faceplus_log_in("username", "password");
|
||||
#endif
|
||||
}
|
||||
|
||||
Faceplus::~Faceplus() {
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void Faceplus::init() {
|
||||
connect(Application::getInstance()->getFaceshift(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled()));
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void Faceplus::reset() {
|
||||
if (_enabled) {
|
||||
QMetaObject::invokeMethod(_reader, "reset");
|
||||
}
|
||||
}
|
||||
|
||||
void Faceplus::setState(const glm::vec3& headTranslation, const glm::quat& headRotation,
|
||||
float estimatedEyePitch, float estimatedEyeYaw, const QVector<float>& blendshapeCoefficients) {
|
||||
_headTranslation = headTranslation;
|
||||
_headRotation = headRotation;
|
||||
_estimatedEyePitch = estimatedEyePitch;
|
||||
_estimatedEyeYaw = estimatedEyeYaw;
|
||||
_blendshapeCoefficients = blendshapeCoefficients;
|
||||
_active = true;
|
||||
}
|
||||
|
||||
void Faceplus::updateEnabled() {
|
||||
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) &&
|
||||
!(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) &&
|
||||
Application::getInstance()->getFaceshift()->isConnectedOrConnecting()));
|
||||
}
|
||||
|
||||
void Faceplus::setEnabled(bool enabled) {
|
||||
if (_enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
if ((_enabled = enabled)) {
|
||||
_reader = new FaceplusReader();
|
||||
QThread* readerThread = new QThread(this);
|
||||
_reader->moveToThread(readerThread);
|
||||
readerThread->start();
|
||||
QMetaObject::invokeMethod(_reader, "init");
|
||||
|
||||
} else {
|
||||
QThread* readerThread = _reader->thread();
|
||||
QMetaObject::invokeMethod(_reader, "shutdown");
|
||||
readerThread->wait();
|
||||
delete readerThread;
|
||||
_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_FACEPLUS
|
||||
static QMultiHash<QByteArray, QPair<int, float> > createChannelNameMap() {
|
||||
QMultiHash<QByteArray, QPair<QByteArray, float> > blendshapeMap;
|
||||
blendshapeMap.insert("EyeBlink_L", QPair<QByteArray, float>("Mix::Blink_Left", 1.0f));
|
||||
blendshapeMap.insert("EyeBlink_R", QPair<QByteArray, float>("Mix::Blink_Right", 1.0f));
|
||||
blendshapeMap.insert("BrowsD_L", QPair<QByteArray, float>("Mix::BrowsDown_Left", 1.0f));
|
||||
blendshapeMap.insert("BrowsD_R", QPair<QByteArray, float>("Mix::BrowsDown_Right", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsIn_Left", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsIn_Right", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsOuterLower_Left", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsOuterLower_Right", 1.0f));
|
||||
blendshapeMap.insert("BrowsU_L", QPair<QByteArray, float>("Mix::BrowsUp_Left", 10.0f));
|
||||
blendshapeMap.insert("BrowsU_R", QPair<QByteArray, float>("Mix::BrowsUp_Right", 10.0f));
|
||||
blendshapeMap.insert("EyeOpen_L", QPair<QByteArray, float>("Mix::EyesWide_Left", 1.0f));
|
||||
blendshapeMap.insert("EyeOpen_R", QPair<QByteArray, float>("Mix::EyesWide_Right", 1.0f));
|
||||
blendshapeMap.insert("MouthFrown_L", QPair<QByteArray, float>("Mix::Frown_Left", 1.0f));
|
||||
blendshapeMap.insert("MouthFrown_R", QPair<QByteArray, float>("Mix::Frown_Right", 1.0f));
|
||||
blendshapeMap.insert("JawLeft", QPair<QByteArray, float>("Mix::Jaw_RotateY_Left", 1.0f));
|
||||
blendshapeMap.insert("JawRight", QPair<QByteArray, float>("Mix::Jaw_RotateY_Right", 1.0f));
|
||||
blendshapeMap.insert("LipsLowerDown", QPair<QByteArray, float>("Mix::LowerLipDown_Left", 0.5f));
|
||||
blendshapeMap.insert("LipsLowerDown", QPair<QByteArray, float>("Mix::LowerLipDown_Right", 0.5f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::LowerLipIn", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::LowerLipOut", 1.0f));
|
||||
blendshapeMap.insert("MouthLeft", QPair<QByteArray, float>("Mix::Midmouth_Left", 1.0f));
|
||||
blendshapeMap.insert("MouthRight", QPair<QByteArray, float>("Mix::Midmouth_Right", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthDown", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthNarrow_Left", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthNarrow_Right", 1.0f));
|
||||
blendshapeMap.insert("JawOpen", QPair<QByteArray, float>("Mix::MouthOpen", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthUp", 1.0f));
|
||||
blendshapeMap.insert("LipsPucker", QPair<QByteArray, float>("Mix::MouthWhistle_NarrowAdjust_Left", 0.5f));
|
||||
blendshapeMap.insert("LipsPucker", QPair<QByteArray, float>("Mix::MouthWhistle_NarrowAdjust_Right", 0.5f));
|
||||
blendshapeMap.insert("Sneer", QPair<QByteArray, float>("Mix::NoseScrunch_Left", 0.5f));
|
||||
blendshapeMap.insert("Sneer", QPair<QByteArray, float>("Mix::NoseScrunch_Right", 0.5f));
|
||||
blendshapeMap.insert("MouthSmile_L", QPair<QByteArray, float>("Mix::Smile_Left", 1.0f));
|
||||
blendshapeMap.insert("MouthSmile_R", QPair<QByteArray, float>("Mix::Smile_Right", 1.0f));
|
||||
blendshapeMap.insert("EyeSquint_L", QPair<QByteArray, float>("Mix::Squint_Left", 1.0f));
|
||||
blendshapeMap.insert("EyeSquint_R", QPair<QByteArray, float>("Mix::Squint_Right", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::UpperLipIn", 1.0f));
|
||||
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::UpperLipOut", 1.0f));
|
||||
blendshapeMap.insert("LipsUpperUp", QPair<QByteArray, float>("Mix::UpperLipUp_Left", 0.5f));
|
||||
blendshapeMap.insert("LipsUpperUp", QPair<QByteArray, float>("Mix::UpperLipUp_Right", 0.5f));
|
||||
|
||||
QMultiHash<QByteArray, QPair<int, float> > channelNameMap;
|
||||
for (int i = 0;; i++) {
|
||||
QByteArray blendshape = FACESHIFT_BLENDSHAPES[i];
|
||||
if (blendshape.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
for (QMultiHash<QByteArray, QPair<QByteArray, float> >::const_iterator it = blendshapeMap.constFind(blendshape);
|
||||
it != blendshapeMap.constEnd() && it.key() == blendshape; it++) {
|
||||
channelNameMap.insert(it.value().first, QPair<int, float>(i, it.value().second));
|
||||
}
|
||||
}
|
||||
|
||||
return channelNameMap;
|
||||
}
|
||||
|
||||
static const QMultiHash<QByteArray, QPair<int, float> >& getChannelNameMap() {
|
||||
static QMultiHash<QByteArray, QPair<int, float> > channelNameMap = createChannelNameMap();
|
||||
return channelNameMap;
|
||||
}
|
||||
#endif
|
||||
|
||||
FaceplusReader::~FaceplusReader() {
|
||||
#ifdef HAVE_FACEPLUS
|
||||
if (faceplus_teardown()) {
|
||||
qDebug() << "Faceplus torn down.";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FaceplusReader::init() {
|
||||
#ifdef HAVE_FACEPLUS
|
||||
if (!faceplus_init("hHD")) {
|
||||
qDebug() << "Failed to initialized Faceplus.";
|
||||
return;
|
||||
}
|
||||
qDebug() << "Faceplus initialized.";
|
||||
|
||||
int channelCount = faceplus_output_channels_count();
|
||||
_outputVector.resize(channelCount);
|
||||
|
||||
int maxIndex = -1;
|
||||
_channelIndexMap.clear();
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
QByteArray name = faceplus_output_channel_name(i);
|
||||
if (name == "Head_Joint::Rotation_X") {
|
||||
_headRotationIndices[0] = i;
|
||||
|
||||
} else if (name == "Head_Joint::Rotation_Y") {
|
||||
_headRotationIndices[1] = i;
|
||||
|
||||
} else if (name == "Head_Joint::Rotation_Z") {
|
||||
_headRotationIndices[2] = i;
|
||||
|
||||
} else if (name == "Left_Eye_Joint::Rotation_X") {
|
||||
_leftEyeRotationIndices[0] = i;
|
||||
|
||||
} else if (name == "Left_Eye_Joint::Rotation_Y") {
|
||||
_leftEyeRotationIndices[1] = i;
|
||||
|
||||
} else if (name == "Right_Eye_Joint::Rotation_X") {
|
||||
_rightEyeRotationIndices[0] = i;
|
||||
|
||||
} else if (name == "Right_Eye_Joint::Rotation_Y") {
|
||||
_rightEyeRotationIndices[1] = i;
|
||||
}
|
||||
for (QMultiHash<QByteArray, QPair<int, float> >::const_iterator it = getChannelNameMap().constFind(name);
|
||||
it != getChannelNameMap().constEnd() && it.key() == name; it++) {
|
||||
_channelIndexMap.insert(i, it.value());
|
||||
maxIndex = qMax(maxIndex, it.value().first);
|
||||
}
|
||||
}
|
||||
_blendshapeCoefficients.resize(maxIndex + 1);
|
||||
_referenceInitialized = false;
|
||||
|
||||
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FaceplusReader::shutdown() {
|
||||
deleteLater();
|
||||
thread()->quit();
|
||||
}
|
||||
|
||||
void FaceplusReader::update() {
|
||||
#ifdef HAVE_FACEPLUS
|
||||
float x, y, rotation, scale;
|
||||
if (!(faceplus_synchronous_track() && faceplus_current_face_location(&x, &y, &rotation, &scale) && !glm::isnan(x) &&
|
||||
faceplus_current_output_vector(_outputVector.data()))) {
|
||||
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (!_referenceInitialized) {
|
||||
_referenceX = x;
|
||||
_referenceY = y;
|
||||
_referenceInitialized = true;
|
||||
}
|
||||
const float TRANSLATION_SCALE = 10.0f;
|
||||
glm::vec3 headTranslation((x - _referenceX) * TRANSLATION_SCALE, (y - _referenceY) * TRANSLATION_SCALE, 0.0f);
|
||||
glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]),
|
||||
_outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2]))));
|
||||
float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) +
|
||||
_outputVector.at(_rightEyeRotationIndices[0])) * -0.5f;
|
||||
float estimatedEyeYaw = (_outputVector.at(_leftEyeRotationIndices[1]) +
|
||||
_outputVector.at(_rightEyeRotationIndices[1])) * 0.5f;
|
||||
|
||||
qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f);
|
||||
for (int i = 0; i < _outputVector.size(); i++) {
|
||||
for (QMultiHash<int, QPair<int, float> >::const_iterator it = _channelIndexMap.constFind(i);
|
||||
it != _channelIndexMap.constEnd() && it.key() == i; it++) {
|
||||
_blendshapeCoefficients[it.value().first] += _outputVector.at(i) * it.value().second;
|
||||
}
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(Application::getInstance()->getFaceplus(), "setState", Q_ARG(const glm::vec3&, headTranslation),
|
||||
Q_ARG(const glm::quat&, headRotation), Q_ARG(float, estimatedEyePitch), Q_ARG(float, estimatedEyeYaw),
|
||||
Q_ARG(const QVector<float>&, _blendshapeCoefficients));
|
||||
|
||||
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FaceplusReader::reset() {
|
||||
#ifdef HAVE_FACEPLUS
|
||||
_referenceInitialized = false;
|
||||
#endif
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
//
|
||||
// Faceplus.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/9/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Faceplus_h
|
||||
#define hifi_Faceplus_h
|
||||
|
||||
#include <QMultiHash>
|
||||
#include <QPair>
|
||||
#include <QVector>
|
||||
|
||||
#include "FaceTracker.h"
|
||||
|
||||
class FaceplusReader;
|
||||
|
||||
/// Interface for Mixamo FacePlus.
|
||||
class Faceplus : public FaceTracker {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Faceplus();
|
||||
virtual ~Faceplus();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
bool isActive() const { return _active; }
|
||||
|
||||
Q_INVOKABLE void setState(const glm::vec3& headTranslation, const glm::quat& headRotation,
|
||||
float estimatedEyePitch, float estimatedEyeYaw, const QVector<float>& blendshapeCoefficients);
|
||||
|
||||
public slots:
|
||||
|
||||
void updateEnabled();
|
||||
|
||||
private:
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
bool _enabled;
|
||||
bool _active;
|
||||
|
||||
FaceplusReader* _reader;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QVector<float>)
|
||||
|
||||
/// The reader object that lives in its own thread.
|
||||
class FaceplusReader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
virtual ~FaceplusReader();
|
||||
|
||||
Q_INVOKABLE void init();
|
||||
Q_INVOKABLE void shutdown();
|
||||
Q_INVOKABLE void update();
|
||||
Q_INVOKABLE void reset();
|
||||
|
||||
private:
|
||||
|
||||
#ifdef HAVE_FACEPLUS
|
||||
QMultiHash<int, QPair<int, float> > _channelIndexMap;
|
||||
QVector<float> _outputVector;
|
||||
int _headRotationIndices[3];
|
||||
int _leftEyeRotationIndices[2];
|
||||
int _rightEyeRotationIndices[2];
|
||||
float _referenceX;
|
||||
float _referenceY;
|
||||
bool _referenceInitialized;
|
||||
QVector<float> _blendshapeCoefficients;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // hifi_Faceplus_h
|
|
@ -15,15 +15,15 @@
|
|||
|
||||
#include "Joystick.h"
|
||||
|
||||
const float MAX_AXIS = 32768.0f;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
const float MAX_AXIS = 32768.0f;
|
||||
|
||||
Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
|
||||
_instanceId(instanceId),
|
||||
_name(name),
|
||||
_sdlGameController(sdlGameController),
|
||||
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
|
||||
_instanceId(instanceId),
|
||||
_name(name),
|
||||
_axes(QVector<float>(SDL_JoystickNumAxes(_sdlJoystick))),
|
||||
_buttons(QVector<bool>(SDL_JoystickNumButtons(_sdlJoystick)))
|
||||
{
|
||||
|
|
|
@ -78,6 +78,8 @@ void OculusManager::connect() {
|
|||
#ifdef HAVE_LIBOVR
|
||||
_calibrationState = UNCALIBRATED;
|
||||
|
||||
qDebug() << "Oculus SDK" << OVR_VERSION_STRING;
|
||||
|
||||
ovr_Initialize();
|
||||
|
||||
_ovrHmd = ovrHmd_Create(0);
|
||||
|
|
|
@ -170,7 +170,6 @@ void Visage::reset() {
|
|||
|
||||
void Visage::updateEnabled() {
|
||||
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) &&
|
||||
!Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) &&
|
||||
!(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) &&
|
||||
Application::getInstance()->getFaceshift()->isConnectedOrConnecting()));
|
||||
}
|
||||
|
|
|
@ -162,36 +162,6 @@ void renderElementProxy(EntityTreeElement* entityTreeElement) {
|
|||
}
|
||||
}
|
||||
|
||||
float EntityTreeRenderer::distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const {
|
||||
glm::vec3 temp = viewFrustum.getPosition() - center;
|
||||
float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp));
|
||||
return distanceToVoxelCenter;
|
||||
}
|
||||
|
||||
// TODO: This could be optimized to be a table, or something that doesn't require recalculation on every
|
||||
// render call for every entity
|
||||
// TODO: This is essentially the same logic used to render voxels, but since models are more detailed then voxels
|
||||
// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it.
|
||||
bool EntityTreeRenderer::shouldRenderEntity(float largestDimension, float distanceToCamera) const {
|
||||
const float voxelToModelRatio = 4.0f; // must be this many times closer to a model than a voxel to see it.
|
||||
float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale();
|
||||
int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust();
|
||||
|
||||
float scale = (float)TREE_SCALE;
|
||||
float visibleDistanceAtScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, voxelSizeScale) / voxelToModelRatio;
|
||||
|
||||
while (scale > largestDimension) {
|
||||
scale /= 2.0f;
|
||||
visibleDistanceAtScale /= 2.0f;
|
||||
}
|
||||
|
||||
if (scale < largestDimension) {
|
||||
visibleDistanceAtScale *= 2.0f;
|
||||
}
|
||||
|
||||
return (distanceToCamera <= visibleDistanceAtScale);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) {
|
||||
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
|
||||
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
|
||||
|
@ -287,7 +257,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
|
||||
// TODO: some entity types (like lights) might want to be rendered even
|
||||
// when they are outside of the view frustum...
|
||||
float distance = distanceToCamera(entityBox.calcCenter(), *args->_viewFrustum);
|
||||
float distance = args->_viewFrustum->distanceToCamera(entityBox.calcCenter());
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "------- renderElement() ----------";
|
||||
|
@ -299,23 +269,28 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
qDebug() << " entityBox:" << entityItem->getAABox();
|
||||
qDebug() << " dimensions:" << entityItem->getDimensionsInMeters() << "in meters";
|
||||
qDebug() << " largestDimension:" << entityBox.getLargestDimension() << "in meters";
|
||||
qDebug() << " shouldRender:" << shouldRenderEntity(entityBox.getLargestDimension(), distance);
|
||||
qDebug() << " shouldRender:" << Menu::getInstance()->shouldRenderMesh(entityBox.getLargestDimension(), distance);
|
||||
qDebug() << " in frustum:" << (args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE);
|
||||
}
|
||||
|
||||
if (shouldRenderEntity(entityBox.getLargestDimension(), distance) &&
|
||||
args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE) {
|
||||
|
||||
|
||||
renderProxies(entityItem, args);
|
||||
|
||||
Glower* glower = NULL;
|
||||
if (entityItem->getGlowLevel() > 0.0f) {
|
||||
glower = new Glower(entityItem->getGlowLevel());
|
||||
}
|
||||
entityItem->render(args);
|
||||
if (glower) {
|
||||
delete glower;
|
||||
bool outOfView = args->_viewFrustum->boxInFrustum(entityBox) == ViewFrustum::OUTSIDE;
|
||||
if (!outOfView) {
|
||||
bool bigEnoughToRender = Menu::getInstance()->shouldRenderMesh(entityBox.getLargestDimension(), distance);
|
||||
|
||||
if (bigEnoughToRender) {
|
||||
renderProxies(entityItem, args);
|
||||
|
||||
Glower* glower = NULL;
|
||||
if (entityItem->getGlowLevel() > 0.0f) {
|
||||
glower = new Glower(entityItem->getGlowLevel());
|
||||
}
|
||||
entityItem->render(args);
|
||||
args->_itemsRendered++;
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
} else {
|
||||
args->_itemsTooSmall++;
|
||||
}
|
||||
} else {
|
||||
args->_itemsOutOfView++;
|
||||
|
|
|
@ -76,11 +76,7 @@ public:
|
|||
void deleteReleasedModels();
|
||||
private:
|
||||
QList<Model*> _releasedModels;
|
||||
|
||||
float distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const;
|
||||
bool shouldRenderEntity(float largestDimension, float distanceToCamera) const;
|
||||
void renderProxies(const EntityItem* entity, RenderArgs* args);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTreeRenderer_h
|
||||
|
|
|
@ -59,7 +59,7 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c
|
|||
|
||||
|
||||
void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableModelEntityItem::render");
|
||||
PerformanceTimer perfTimer("RMEIrender");
|
||||
assert(getType() == EntityTypes::Model);
|
||||
|
||||
bool drawAsModel = hasModel();
|
||||
|
@ -119,7 +119,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
// TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render
|
||||
// is significantly more expensive. Is there a way to call this that doesn't cost us as much?
|
||||
PerformanceTimer perfTimer("model->render");
|
||||
_model->render(alpha, modelRenderMode);
|
||||
_model->render(alpha, modelRenderMode, args);
|
||||
} else {
|
||||
// if we couldn't get a model, then just draw a cube
|
||||
glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
GlowEffect::GlowEffect()
|
||||
: _initialized(false),
|
||||
_renderMode(DIFFUSE_ADD_MODE),
|
||||
_isOddFrame(false),
|
||||
_isFirstFrame(true),
|
||||
_intensity(0.0f) {
|
||||
|
@ -41,9 +40,9 @@ GlowEffect::~GlowEffect() {
|
|||
}
|
||||
|
||||
QOpenGLFramebufferObject* GlowEffect::getFreeFramebufferObject() const {
|
||||
return (_renderMode == DIFFUSE_ADD_MODE && !_isOddFrame) ?
|
||||
Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject() :
|
||||
Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject();
|
||||
return (_isOddFrame ?
|
||||
Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject():
|
||||
Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject());
|
||||
}
|
||||
|
||||
static ProgramObject* createProgram(const QString& name) {
|
||||
|
@ -137,31 +136,28 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
|
||||
QOpenGLFramebufferObject* destFBO = toTexture ?
|
||||
Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject() : NULL;
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect) || (_isEmpty && _renderMode != DIFFUSE_ADD_MODE)) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect) || _isEmpty) {
|
||||
// copy the primary to the screen
|
||||
if (QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) {
|
||||
if (destFBO && QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) {
|
||||
QOpenGLFramebufferObject::blitFramebuffer(destFBO, primaryFBO);
|
||||
} else {
|
||||
maybeBind(destFBO);
|
||||
if (!destFBO) {
|
||||
glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(),
|
||||
Application::getInstance()->getGLWidget()->getDeviceHeight());
|
||||
}
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_LIGHTING);
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
renderFullscreenQuad();
|
||||
renderFullscreenQuad();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_LIGHTING);
|
||||
maybeRelease(destFBO);
|
||||
}
|
||||
} else if (_renderMode == ADD_MODE) {
|
||||
maybeBind(destFBO);
|
||||
_addProgram->bind();
|
||||
renderFullscreenQuad();
|
||||
_addProgram->release();
|
||||
maybeRelease(destFBO);
|
||||
|
||||
} else if (_renderMode == DIFFUSE_ADD_MODE) {
|
||||
} else {
|
||||
// diffuse into the secondary/tertiary (alternating between frames)
|
||||
QOpenGLFramebufferObject* oldDiffusedFBO =
|
||||
Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject();
|
||||
|
@ -197,6 +193,11 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
destFBO = oldDiffusedFBO;
|
||||
}
|
||||
maybeBind(destFBO);
|
||||
if (!destFBO) {
|
||||
glViewport(0, 0,
|
||||
Application::getInstance()->getGLWidget()->getDeviceWidth(),
|
||||
Application::getInstance()->getGLWidget()->getDeviceHeight());
|
||||
}
|
||||
_addSeparateProgram->bind();
|
||||
renderFullscreenQuad();
|
||||
_addSeparateProgram->release();
|
||||
|
@ -205,70 +206,6 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
} else { // _renderMode == BLUR_ADD_MODE || _renderMode == BLUR_PERSIST_ADD_MODE
|
||||
// render the primary to the secondary with the horizontal blur
|
||||
QOpenGLFramebufferObject* secondaryFBO =
|
||||
Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject();
|
||||
secondaryFBO->bind();
|
||||
|
||||
_horizontalBlurProgram->bind();
|
||||
renderFullscreenQuad();
|
||||
_horizontalBlurProgram->release();
|
||||
|
||||
secondaryFBO->release();
|
||||
|
||||
if (_renderMode == BLUR_ADD_MODE) {
|
||||
// render the secondary to the screen with the vertical blur
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, secondaryFBO->texture());
|
||||
|
||||
if (toTexture) {
|
||||
destFBO = Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject();
|
||||
}
|
||||
maybeBind(destFBO);
|
||||
_verticalBlurAddProgram->bind();
|
||||
renderFullscreenQuad();
|
||||
_verticalBlurAddProgram->release();
|
||||
maybeRelease(destFBO);
|
||||
|
||||
} else { // _renderMode == BLUR_PERSIST_ADD_MODE
|
||||
// render the secondary to the tertiary with vertical blur and persistence
|
||||
QOpenGLFramebufferObject* tertiaryFBO =
|
||||
Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject();
|
||||
tertiaryFBO->bind();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE_MINUS_CONSTANT_ALPHA, GL_CONSTANT_ALPHA);
|
||||
const float PERSISTENCE_SMOOTHING = 0.9f;
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, _isFirstFrame ? 0.0f : PERSISTENCE_SMOOTHING);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, secondaryFBO->texture());
|
||||
|
||||
_verticalBlurProgram->bind();
|
||||
renderFullscreenQuad();
|
||||
_verticalBlurProgram->release();
|
||||
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
// now add the tertiary to the primary buffer
|
||||
tertiaryFBO->release();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, tertiaryFBO->texture());
|
||||
|
||||
maybeBind(destFBO);
|
||||
_addSeparateProgram->bind();
|
||||
renderFullscreenQuad();
|
||||
_addSeparateProgram->release();
|
||||
maybeRelease(destFBO);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
@ -286,28 +223,6 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
return destFBO;
|
||||
}
|
||||
|
||||
void GlowEffect::cycleRenderMode() {
|
||||
switch(_renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT)) {
|
||||
case ADD_MODE:
|
||||
qDebug() << "Glow mode: Add";
|
||||
break;
|
||||
|
||||
case BLUR_ADD_MODE:
|
||||
qDebug() << "Glow mode: Blur/add";
|
||||
break;
|
||||
|
||||
case BLUR_PERSIST_ADD_MODE:
|
||||
qDebug() << "Glow mode: Blur/persist/add";
|
||||
break;
|
||||
|
||||
default:
|
||||
case DIFFUSE_ADD_MODE:
|
||||
qDebug() << "Glow mode: Diffuse/add";
|
||||
break;
|
||||
}
|
||||
_isFirstFrame = true;
|
||||
}
|
||||
|
||||
Glower::Glower(float amount) {
|
||||
Application::getInstance()->getGlowEffect()->begin(amount);
|
||||
}
|
||||
|
|
|
@ -51,17 +51,10 @@ public:
|
|||
/// \return the framebuffer object to which we rendered, or NULL if to the frame buffer
|
||||
QOpenGLFramebufferObject* render(bool toTexture = false);
|
||||
|
||||
public slots:
|
||||
|
||||
void cycleRenderMode();
|
||||
|
||||
private:
|
||||
|
||||
enum RenderMode { ADD_MODE, BLUR_ADD_MODE, BLUR_PERSIST_ADD_MODE, DIFFUSE_ADD_MODE, RENDER_MODE_COUNT };
|
||||
|
||||
|
||||
bool _initialized;
|
||||
|
||||
RenderMode _renderMode;
|
||||
ProgramObject* _addProgram;
|
||||
ProgramObject* _horizontalBlurProgram;
|
||||
ProgramObject* _verticalBlurAddProgram;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <CapsuleShape.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PhysicsEntity.h>
|
||||
#include <ShapeCollider.h>
|
||||
#include <SphereShape.h>
|
||||
|
@ -44,7 +45,9 @@ Model::Model(QObject* parent) :
|
|||
_pupilDilation(0.0f),
|
||||
_url("http://invalid.com"),
|
||||
_blendNumber(0),
|
||||
_appliedBlendNumber(0) {
|
||||
_appliedBlendNumber(0),
|
||||
_calculatedMeshBoxesValid(false),
|
||||
_meshGroupsKnown(false) {
|
||||
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
|
@ -269,6 +272,8 @@ void Model::reset() {
|
|||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
|
||||
}
|
||||
|
||||
_meshGroupsKnown = false;
|
||||
}
|
||||
|
||||
bool Model::updateGeometry() {
|
||||
|
@ -318,6 +323,7 @@ bool Model::updateGeometry() {
|
|||
deleteGeometry();
|
||||
_dilatedTextures.clear();
|
||||
_geometry = geometry;
|
||||
_meshGroupsKnown = false;
|
||||
setJointStates(newJointStates);
|
||||
needToRebuild = true;
|
||||
} else if (_jointStates.isEmpty()) {
|
||||
|
@ -384,7 +390,7 @@ void Model::setJointStates(QVector<JointState> states) {
|
|||
_boundingRadius = radius;
|
||||
}
|
||||
|
||||
bool Model::render(float alpha, RenderMode mode) {
|
||||
bool Model::render(float alpha, RenderMode mode, RenderArgs* args) {
|
||||
// render the attachments
|
||||
foreach (Model* attachment, _attachments) {
|
||||
attachment->render(alpha, mode);
|
||||
|
@ -392,6 +398,23 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
if (_meshStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we don't have valid mesh boxes, calculate them now, this only matters in cases
|
||||
// where our caller has passed RenderArgs which will include a view frustum we can cull
|
||||
// against. We cache the results of these calculations so long as the model hasn't been
|
||||
// simulated and the mesh hasn't changed.
|
||||
if (args && !_calculatedMeshBoxesValid) {
|
||||
PerformanceTimer perfTimer("calculatedMeshBoxes");
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
int numberOfMeshes = geometry.meshes.size();
|
||||
_calculatedMeshBoxes.resize(numberOfMeshes);
|
||||
for (int i = 0; i < numberOfMeshes; i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents);
|
||||
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
|
||||
}
|
||||
_calculatedMeshBoxesValid = true;
|
||||
}
|
||||
|
||||
// set up dilated textures on first render after load/simulate
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
@ -402,6 +425,10 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
_dilatedTextures.append(dilated);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_meshGroupsKnown) {
|
||||
segregateMeshGroups();
|
||||
}
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
@ -431,11 +458,31 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
mode == DEFAULT_RENDER_MODE || mode == NORMAL_RENDER_MODE,
|
||||
mode == DEFAULT_RENDER_MODE);
|
||||
|
||||
renderMeshes(mode, false);
|
||||
const float DEFAULT_ALPHA_THRESHOLD = 0.5f;
|
||||
|
||||
//renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, book isSkinned, args);
|
||||
int opaqueMeshPartsRendered = 0;
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args);
|
||||
opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args);
|
||||
|
||||
// render translucent meshes afterwards
|
||||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true);
|
||||
renderMeshes(mode, true, 0.75f);
|
||||
int translucentMeshPartsRendered = 0;
|
||||
const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
|
@ -445,7 +492,15 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true);
|
||||
|
||||
if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) {
|
||||
renderMeshes(mode, true, 0.0f);
|
||||
const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f;
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args);
|
||||
translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args);
|
||||
}
|
||||
|
||||
glDepthMask(true);
|
||||
|
@ -468,6 +523,11 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
|
||||
// restore all the default material settings
|
||||
Application::getInstance()->setupWorldLight();
|
||||
|
||||
if (args) {
|
||||
args->_translucentMeshPartsRendered = translucentMeshPartsRendered;
|
||||
args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -511,6 +571,22 @@ Extents Model::getUnscaledMeshExtents() const {
|
|||
return scaledExtents;
|
||||
}
|
||||
|
||||
Extents Model::calculateScaledOffsetExtents(const Extents& extents) const {
|
||||
// we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
|
||||
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
|
||||
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
|
||||
|
||||
Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
|
||||
((maximum + _offset) * _scale) };
|
||||
|
||||
Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation);
|
||||
|
||||
Extents translatedExtents = { rotatedExtents.minimum + _translation,
|
||||
rotatedExtents.maximum + _translation };
|
||||
return translatedExtents;
|
||||
}
|
||||
|
||||
|
||||
bool Model::getJointState(int index, glm::quat& rotation) const {
|
||||
if (index == -1 || index >= _jointStates.size()) {
|
||||
return false;
|
||||
|
@ -790,6 +866,8 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
|||
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
|
||||
|
||||
if (isActive() && fullUpdate) {
|
||||
_calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid
|
||||
|
||||
// check for scale to fit
|
||||
if (_scaleToFit && !_scaledToFit) {
|
||||
scaleToFit();
|
||||
|
@ -1169,6 +1247,7 @@ void Model::applyNextGeometry() {
|
|||
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
|
||||
_baseGeometry = _nextBaseGeometry;
|
||||
_geometry = _nextGeometry;
|
||||
_meshGroupsKnown = false;
|
||||
_nextBaseGeometry.reset();
|
||||
_nextGeometry.reset();
|
||||
}
|
||||
|
@ -1200,19 +1279,358 @@ void Model::deleteGeometry() {
|
|||
_blendedBlendshapeCoefficients.clear();
|
||||
}
|
||||
|
||||
void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold) {
|
||||
void Model::segregateMeshGroups() {
|
||||
_meshesTranslucentTangents.clear();
|
||||
_meshesTranslucent.clear();
|
||||
_meshesTranslucentTangentsSpecular.clear();
|
||||
_meshesTranslucentSpecular.clear();
|
||||
|
||||
_meshesTranslucentTangentsSkinned.clear();
|
||||
_meshesTranslucentSkinned.clear();
|
||||
_meshesTranslucentTangentsSpecularSkinned.clear();
|
||||
_meshesTranslucentSpecularSkinned.clear();
|
||||
|
||||
_meshesOpaqueTangents.clear();
|
||||
_meshesOpaque.clear();
|
||||
_meshesOpaqueTangentsSpecular.clear();
|
||||
_meshesOpaqueSpecular.clear();
|
||||
|
||||
_meshesOpaqueTangentsSkinned.clear();
|
||||
_meshesOpaqueSkinned.clear();
|
||||
_meshesOpaqueTangentsSpecularSkinned.clear();
|
||||
_meshesOpaqueSpecularSkinned.clear();
|
||||
|
||||
_unsortedMeshesTranslucentTangents.clear();
|
||||
_unsortedMeshesTranslucent.clear();
|
||||
_unsortedMeshesTranslucentTangentsSpecular.clear();
|
||||
_unsortedMeshesTranslucentSpecular.clear();
|
||||
|
||||
_unsortedMeshesTranslucentTangentsSkinned.clear();
|
||||
_unsortedMeshesTranslucentSkinned.clear();
|
||||
_unsortedMeshesTranslucentTangentsSpecularSkinned.clear();
|
||||
_unsortedMeshesTranslucentSpecularSkinned.clear();
|
||||
|
||||
_unsortedMeshesOpaqueTangents.clear();
|
||||
_unsortedMeshesOpaque.clear();
|
||||
_unsortedMeshesOpaqueTangentsSpecular.clear();
|
||||
_unsortedMeshesOpaqueSpecular.clear();
|
||||
|
||||
_unsortedMeshesOpaqueTangentsSkinned.clear();
|
||||
_unsortedMeshesOpaqueSkinned.clear();
|
||||
_unsortedMeshesOpaqueTangentsSpecularSkinned.clear();
|
||||
_unsortedMeshesOpaqueSpecularSkinned.clear();
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||
|
||||
|
||||
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||
for (int i = 0; i < networkMeshes.size(); i++) {
|
||||
const NetworkMesh& networkMesh = networkMeshes.at(i);
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
const MeshState& state = _meshStates.at(i);
|
||||
|
||||
bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size();
|
||||
bool hasTangents = !mesh.tangents.isEmpty();
|
||||
bool hasSpecular = mesh.hasSpecularTexture();
|
||||
bool isSkinned = state.clusterMatrices.size() > 1;
|
||||
QString materialID;
|
||||
|
||||
// create a material name from all the parts. If there's one part, this will be a single material and its
|
||||
// true name. If however the mesh has multiple parts the name will be all the part's materials mashed together
|
||||
// which will result in those parts being sorted away from single material parts.
|
||||
QString lastPartMaterialID;
|
||||
foreach(FBXMeshPart part, mesh.parts) {
|
||||
if (part.materialID != lastPartMaterialID) {
|
||||
materialID += part.materialID;
|
||||
}
|
||||
lastPartMaterialID = part.materialID;
|
||||
}
|
||||
const bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug() << "materialID:" << materialID << "parts:" << mesh.parts.size();
|
||||
}
|
||||
|
||||
if (translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucent.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentTangents.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && hasTangents && hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentTangentsSpecular.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentSpecular.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && hasTangents && !hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentTangentsSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && !hasTangents && !hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && hasTangents && hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentTangentsSpecularSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (translucentMesh && !hasTangents && hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesTranslucentSpecularSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaque.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueTangents.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && hasTangents && hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueTangentsSpecular.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueSpecular.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && hasTangents && !hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueTangentsSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && !hasTangents && !hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && hasTangents && hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueTangentsSpecularSkinned.insertMulti(materialID, i);
|
||||
|
||||
} else if (!translucentMesh && !hasTangents && hasSpecular && isSkinned) {
|
||||
|
||||
_unsortedMeshesOpaqueSpecularSkinned.insertMulti(materialID, i);
|
||||
} else {
|
||||
qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???";
|
||||
}
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucent) {
|
||||
_meshesTranslucent.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentTangents) {
|
||||
_meshesTranslucentTangents.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentTangentsSpecular) {
|
||||
_meshesTranslucentTangentsSpecular.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentSpecular) {
|
||||
_meshesTranslucentSpecular.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentSkinned) {
|
||||
_meshesTranslucentSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentTangentsSkinned) {
|
||||
_meshesTranslucentTangentsSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentTangentsSpecularSkinned) {
|
||||
_meshesTranslucentTangentsSpecularSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesTranslucentSpecularSkinned) {
|
||||
_meshesTranslucentSpecularSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaque) {
|
||||
_meshesOpaque.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueTangents) {
|
||||
_meshesOpaqueTangents.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueTangentsSpecular) {
|
||||
_meshesOpaqueTangentsSpecular.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueSpecular) {
|
||||
_meshesOpaqueSpecular.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueSkinned) {
|
||||
_meshesOpaqueSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueTangentsSkinned) {
|
||||
_meshesOpaqueTangentsSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueTangentsSpecularSkinned) {
|
||||
_meshesOpaqueTangentsSpecularSkinned.append(i);
|
||||
}
|
||||
|
||||
foreach(int i, _unsortedMeshesOpaqueSpecularSkinned) {
|
||||
_meshesOpaqueSpecularSkinned.append(i);
|
||||
}
|
||||
|
||||
_unsortedMeshesTranslucentTangents.clear();
|
||||
_unsortedMeshesTranslucent.clear();
|
||||
_unsortedMeshesTranslucentTangentsSpecular.clear();
|
||||
_unsortedMeshesTranslucentSpecular.clear();
|
||||
|
||||
_unsortedMeshesTranslucentTangentsSkinned.clear();
|
||||
_unsortedMeshesTranslucentSkinned.clear();
|
||||
_unsortedMeshesTranslucentTangentsSpecularSkinned.clear();
|
||||
_unsortedMeshesTranslucentSpecularSkinned.clear();
|
||||
|
||||
_unsortedMeshesOpaqueTangents.clear();
|
||||
_unsortedMeshesOpaque.clear();
|
||||
_unsortedMeshesOpaqueTangentsSpecular.clear();
|
||||
_unsortedMeshesOpaqueSpecular.clear();
|
||||
|
||||
_unsortedMeshesOpaqueTangentsSkinned.clear();
|
||||
_unsortedMeshesOpaqueSkinned.clear();
|
||||
_unsortedMeshesOpaqueTangentsSpecularSkinned.clear();
|
||||
_unsortedMeshesOpaqueSpecularSkinned.clear();
|
||||
|
||||
_meshGroupsKnown = true;
|
||||
}
|
||||
|
||||
int Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold,
|
||||
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) {
|
||||
|
||||
bool dontCullOutOfViewMeshParts = Menu::getInstance()->isOptionChecked(MenuOption::DontCullOutOfViewMeshParts);
|
||||
bool cullTooSmallMeshParts = !Menu::getInstance()->isOptionChecked(MenuOption::DontCullTooSmallMeshParts);
|
||||
bool dontReduceMaterialSwitches = Menu::getInstance()->isOptionChecked(MenuOption::DontReduceMaterialSwitches);
|
||||
|
||||
QString lastMaterialID;
|
||||
int meshPartsRendered = 0;
|
||||
updateVisibleJointStates();
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||
|
||||
// depending on which parameters we were called with, pick the correct mesh group to render
|
||||
QVector<int>* whichList = NULL;
|
||||
if (translucent && !hasTangents && !hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesTranslucent;
|
||||
} else if (translucent && hasTangents && !hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesTranslucentTangents;
|
||||
} else if (translucent && hasTangents && hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesTranslucentTangentsSpecular;
|
||||
} else if (translucent && !hasTangents && hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesTranslucentSpecular;
|
||||
} else if (translucent && hasTangents && !hasSpecular && isSkinned) {
|
||||
whichList = &_meshesTranslucentTangentsSkinned;
|
||||
} else if (translucent && !hasTangents && !hasSpecular && isSkinned) {
|
||||
whichList = &_meshesTranslucentSkinned;
|
||||
} else if (translucent && hasTangents && hasSpecular && isSkinned) {
|
||||
whichList = &_meshesTranslucentTangentsSpecularSkinned;
|
||||
} else if (translucent && !hasTangents && hasSpecular && isSkinned) {
|
||||
whichList = &_meshesTranslucentSpecularSkinned;
|
||||
} else if (!translucent && !hasTangents && !hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesOpaque;
|
||||
} else if (!translucent && hasTangents && !hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesOpaqueTangents;
|
||||
} else if (!translucent && hasTangents && hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesOpaqueTangentsSpecular;
|
||||
} else if (!translucent && !hasTangents && hasSpecular && !isSkinned) {
|
||||
whichList = &_meshesOpaqueSpecular;
|
||||
} else if (!translucent && hasTangents && !hasSpecular && isSkinned) {
|
||||
whichList = &_meshesOpaqueTangentsSkinned;
|
||||
} else if (!translucent && !hasTangents && !hasSpecular && isSkinned) {
|
||||
whichList = &_meshesOpaqueSkinned;
|
||||
} else if (!translucent && hasTangents && hasSpecular && isSkinned) {
|
||||
whichList = &_meshesOpaqueTangentsSpecularSkinned;
|
||||
} else if (!translucent && !hasTangents && hasSpecular && isSkinned) {
|
||||
whichList = &_meshesOpaqueSpecularSkinned;
|
||||
} else {
|
||||
qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???";
|
||||
}
|
||||
|
||||
for (int i = 0; i < networkMeshes.size(); i++) {
|
||||
if (!whichList) {
|
||||
qDebug() << "unexpected!!! we don't know which list of meshes to render...";
|
||||
return 0;
|
||||
}
|
||||
QVector<int>& list = *whichList;
|
||||
|
||||
// If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs
|
||||
if (list.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProgramObject* program = &_program;
|
||||
Locations* locations = &_locations;
|
||||
ProgramObject* skinProgram = &_skinProgram;
|
||||
SkinLocations* skinLocations = &_skinLocations;
|
||||
GLenum specularTextureUnit = 0;
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
program = &_shadowProgram;
|
||||
skinProgram = &_skinShadowProgram;
|
||||
skinLocations = &_skinShadowLocations;
|
||||
} else if (translucent && alphaThreshold == 0.0f) {
|
||||
program = &_translucentProgram;
|
||||
locations = &_translucentLocations;
|
||||
skinProgram = &_skinTranslucentProgram;
|
||||
skinLocations = &_skinTranslucentLocations;
|
||||
|
||||
} else if (hasTangents) {
|
||||
if (hasSpecular) {
|
||||
program = &_normalSpecularMapProgram;
|
||||
locations = &_normalSpecularMapLocations;
|
||||
skinProgram = &_skinNormalSpecularMapProgram;
|
||||
skinLocations = &_skinNormalSpecularMapLocations;
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
} else {
|
||||
program = &_normalMapProgram;
|
||||
locations = &_normalMapLocations;
|
||||
skinProgram = &_skinNormalMapProgram;
|
||||
skinLocations = &_skinNormalMapLocations;
|
||||
}
|
||||
} else if (hasSpecular) {
|
||||
program = &_specularMapProgram;
|
||||
locations = &_specularMapLocations;
|
||||
skinProgram = &_skinSpecularMapProgram;
|
||||
skinLocations = &_skinSpecularMapLocations;
|
||||
specularTextureUnit = GL_TEXTURE1;
|
||||
}
|
||||
|
||||
ProgramObject* activeProgram = program;
|
||||
Locations* activeLocations = locations;
|
||||
|
||||
if (isSkinned) {
|
||||
skinProgram->bind();
|
||||
activeProgram = skinProgram;
|
||||
activeLocations = skinLocations;
|
||||
} else {
|
||||
program->bind();
|
||||
}
|
||||
activeProgram->setUniformValue(activeLocations->alphaThreshold, alphaThreshold);
|
||||
|
||||
// i is the "index" from the original networkMeshes QVector...
|
||||
foreach (int i, list) {
|
||||
|
||||
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
||||
// to false to rebuild out mesh groups.
|
||||
|
||||
if (i < 0 || i >= networkMeshes.size() || i > geometry.meshes.size()) {
|
||||
_meshGroupsKnown = false; // regenerate these lists next time around.
|
||||
continue;
|
||||
}
|
||||
|
||||
// exit early if the translucency doesn't match what we're drawing
|
||||
const NetworkMesh& networkMesh = networkMeshes.at(i);
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
if (translucent ? (networkMesh.getTranslucentPartCount(mesh) == 0) :
|
||||
(networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const_cast<QOpenGLBuffer&>(networkMesh.indexBuffer).bind();
|
||||
|
||||
int vertexCount = mesh.vertices.size();
|
||||
|
@ -1221,54 +1639,40 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold
|
|||
continue;
|
||||
}
|
||||
|
||||
const_cast<QOpenGLBuffer&>(networkMesh.vertexBuffer).bind();
|
||||
|
||||
ProgramObject* program = &_program;
|
||||
Locations* locations = &_locations;
|
||||
ProgramObject* skinProgram = &_skinProgram;
|
||||
SkinLocations* skinLocations = &_skinLocations;
|
||||
GLenum specularTextureUnit = 0;
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
program = &_shadowProgram;
|
||||
skinProgram = &_skinShadowProgram;
|
||||
skinLocations = &_skinShadowLocations;
|
||||
|
||||
} else if (translucent && alphaThreshold == 0.0f) {
|
||||
program = &_translucentProgram;
|
||||
locations = &_translucentLocations;
|
||||
skinProgram = &_skinTranslucentProgram;
|
||||
skinLocations = &_skinTranslucentLocations;
|
||||
|
||||
} else if (!mesh.tangents.isEmpty()) {
|
||||
if (mesh.hasSpecularTexture()) {
|
||||
program = &_normalSpecularMapProgram;
|
||||
locations = &_normalSpecularMapLocations;
|
||||
skinProgram = &_skinNormalSpecularMapProgram;
|
||||
skinLocations = &_skinNormalSpecularMapLocations;
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
|
||||
} else {
|
||||
program = &_normalMapProgram;
|
||||
locations = &_normalMapLocations;
|
||||
skinProgram = &_skinNormalMapProgram;
|
||||
skinLocations = &_skinNormalMapLocations;
|
||||
// if we got here, then check to see if this mesh is in view
|
||||
if (args) {
|
||||
bool shouldRender = true;
|
||||
args->_meshesConsidered++;
|
||||
|
||||
if (args->_viewFrustum) {
|
||||
shouldRender = dontCullOutOfViewMeshParts ||
|
||||
args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE;
|
||||
if (shouldRender && cullTooSmallMeshParts) {
|
||||
float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter());
|
||||
shouldRender = Menu::getInstance()->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(),
|
||||
distance);
|
||||
if (!shouldRender) {
|
||||
args->_meshesTooSmall++;
|
||||
}
|
||||
} else {
|
||||
args->_meshesOutOfView++;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRender) {
|
||||
args->_meshesRendered++;
|
||||
} else {
|
||||
continue; // skip this mesh
|
||||
}
|
||||
} else if (mesh.hasSpecularTexture()) {
|
||||
program = &_specularMapProgram;
|
||||
locations = &_specularMapLocations;
|
||||
skinProgram = &_skinSpecularMapProgram;
|
||||
skinLocations = &_skinSpecularMapLocations;
|
||||
specularTextureUnit = GL_TEXTURE1;
|
||||
}
|
||||
|
||||
const MeshState& state = _meshStates.at(i);
|
||||
ProgramObject* activeProgram = program;
|
||||
Locations* activeLocations = locations;
|
||||
const_cast<QOpenGLBuffer&>(networkMesh.vertexBuffer).bind();
|
||||
|
||||
glPushMatrix();
|
||||
Application::getInstance()->loadTranslatedViewMatrix(_translation);
|
||||
|
||||
|
||||
const MeshState& state = _meshStates.at(i);
|
||||
if (state.clusterMatrices.size() > 1) {
|
||||
skinProgram->bind();
|
||||
glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false,
|
||||
(const float*)state.clusterMatrices.constData());
|
||||
int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) +
|
||||
|
@ -1279,16 +1683,10 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold
|
|||
offset + vertexCount * sizeof(glm::vec4), 4);
|
||||
skinProgram->enableAttributeArray(skinLocations->clusterIndices);
|
||||
skinProgram->enableAttributeArray(skinLocations->clusterWeights);
|
||||
activeProgram = skinProgram;
|
||||
activeLocations = skinLocations;
|
||||
|
||||
} else {
|
||||
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
|
||||
program->bind();
|
||||
}
|
||||
|
||||
activeProgram->setUniformValue(activeLocations->alphaThreshold, alphaThreshold);
|
||||
|
||||
if (mesh.blendshapes.isEmpty()) {
|
||||
if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) {
|
||||
activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3);
|
||||
|
@ -1333,45 +1731,74 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold
|
|||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
} else {
|
||||
glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity);
|
||||
if (!(translucent && alphaThreshold == 0.0f)) {
|
||||
glAlphaFunc(GL_EQUAL, diffuse.a = Application::getInstance()->getGlowEffect()->getIntensity());
|
||||
}
|
||||
glm::vec4 specular = glm::vec4(part.specularColor, 1.0f);
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse);
|
||||
glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse);
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular);
|
||||
glMaterialf(GL_FRONT, GL_SHININESS, part.shininess);
|
||||
if (dontReduceMaterialSwitches || lastMaterialID != part.materialID) {
|
||||
const bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug() << "Material Changed ---------------------------------------------";
|
||||
qDebug() << "part INDEX:" << j;
|
||||
qDebug() << "NEW part.materialID:" << part.materialID;
|
||||
}
|
||||
|
||||
glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity);
|
||||
if (!(translucent && alphaThreshold == 0.0f)) {
|
||||
glAlphaFunc(GL_EQUAL, diffuse.a = Application::getInstance()->getGlowEffect()->getIntensity());
|
||||
}
|
||||
glm::vec4 specular = glm::vec4(part.specularColor, 1.0f);
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse);
|
||||
glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse);
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular);
|
||||
glMaterialf(GL_FRONT, GL_SHININESS, part.shininess);
|
||||
|
||||
Texture* diffuseMap = networkPart.diffuseTexture.data();
|
||||
if (mesh.isEye && diffuseMap) {
|
||||
diffuseMap = (_dilatedTextures[i][j] =
|
||||
static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, !diffuseMap ?
|
||||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
|
||||
Texture* diffuseMap = networkPart.diffuseTexture.data();
|
||||
if (mesh.isEye && diffuseMap) {
|
||||
diffuseMap = (_dilatedTextures[i][j] =
|
||||
static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, !diffuseMap ?
|
||||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
|
||||
|
||||
if (!mesh.tangents.isEmpty()) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
Texture* normalMap = networkPart.normalTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !normalMap ?
|
||||
Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
if (!mesh.tangents.isEmpty()) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
Texture* normalMap = networkPart.normalTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !normalMap ?
|
||||
Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (specularTextureUnit) {
|
||||
glActiveTexture(specularTextureUnit);
|
||||
Texture* specularMap = networkPart.specularTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !specularMap ?
|
||||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (specularTextureUnit) {
|
||||
glActiveTexture(specularTextureUnit);
|
||||
Texture* specularMap = networkPart.specularTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !specularMap ?
|
||||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
if (args) {
|
||||
args->_materialSwitches++;
|
||||
}
|
||||
|
||||
}
|
||||
lastMaterialID = part.materialID;
|
||||
}
|
||||
|
||||
meshPartsRendered++;
|
||||
|
||||
if (part.quadIndices.size() > 0) {
|
||||
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
|
||||
offset += part.quadIndices.size() * sizeof(int);
|
||||
}
|
||||
|
||||
if (part.triangleIndices.size() > 0) {
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(),
|
||||
GL_UNSIGNED_INT, (void*)offset);
|
||||
offset += part.triangleIndices.size() * sizeof(int);
|
||||
}
|
||||
|
||||
if (args) {
|
||||
const int INDICES_PER_TRIANGLE = 3;
|
||||
const int INDICES_PER_QUAD = 4;
|
||||
args->_trianglesRendered += part.triangleIndices.size() / INDICES_PER_TRIANGLE;
|
||||
args->_quadsRendered += part.quadIndices.size() / INDICES_PER_QUAD;
|
||||
}
|
||||
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
|
||||
offset += part.quadIndices.size() * sizeof(int);
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(),
|
||||
GL_UNSIGNED_INT, (void*)offset);
|
||||
offset += part.triangleIndices.size() * sizeof(int);
|
||||
}
|
||||
|
||||
if (!mesh.colors.isEmpty()) {
|
||||
|
@ -1401,8 +1828,10 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold
|
|||
}
|
||||
glPopMatrix();
|
||||
|
||||
activeProgram->release();
|
||||
}
|
||||
activeProgram->release();
|
||||
|
||||
return meshPartsRendered;
|
||||
}
|
||||
|
||||
void AnimationHandle::setURL(const QUrl& url) {
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include <PhysicsEntity.h>
|
||||
|
||||
#include <AABox.h>
|
||||
#include <AnimationCache.h>
|
||||
#include <PhysicsEntity.h>
|
||||
|
||||
#include "GeometryCache.h"
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -30,6 +30,8 @@ class QScriptEngine;
|
|||
|
||||
class AnimationHandle;
|
||||
class Shape;
|
||||
class RenderArgs;
|
||||
class ViewFrustum;
|
||||
|
||||
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
|
||||
typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
|
||||
|
@ -84,7 +86,7 @@ public:
|
|||
|
||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
|
||||
|
||||
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE);
|
||||
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL);
|
||||
|
||||
/// Sets the URL of the model to render.
|
||||
/// \param fallback the URL of a fallback model to render if the requested model fails to load
|
||||
|
@ -107,6 +109,9 @@ public:
|
|||
/// Returns the unscaled extents of the model's mesh
|
||||
Extents getUnscaledMeshExtents() const;
|
||||
|
||||
/// Returns the scaled equivalent of some extents in model space.
|
||||
Extents calculateScaledOffsetExtents(const Extents& extents) const;
|
||||
|
||||
/// Returns a reference to the shared geometry.
|
||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||
|
||||
|
@ -247,7 +252,7 @@ private:
|
|||
|
||||
void applyNextGeometry();
|
||||
void deleteGeometry();
|
||||
void renderMeshes(RenderMode mode, bool translucent, float alphaThreshold = 0.5f);
|
||||
int renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL);
|
||||
QVector<JointState> createJointStates(const FBXGeometry& geometry);
|
||||
void initJointTransforms();
|
||||
|
||||
|
@ -325,6 +330,54 @@ private:
|
|||
static SkinLocations _skinTranslucentLocations;
|
||||
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
|
||||
|
||||
QVector<AABox> _calculatedMeshBoxes;
|
||||
bool _calculatedMeshBoxesValid;
|
||||
|
||||
void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes
|
||||
|
||||
bool _meshGroupsKnown;
|
||||
|
||||
QMap<QString, int> _unsortedMeshesTranslucent;
|
||||
QMap<QString, int> _unsortedMeshesTranslucentTangents;
|
||||
QMap<QString, int> _unsortedMeshesTranslucentTangentsSpecular;
|
||||
QMap<QString, int> _unsortedMeshesTranslucentSpecular;
|
||||
|
||||
QMap<QString, int> _unsortedMeshesTranslucentSkinned;
|
||||
QMap<QString, int> _unsortedMeshesTranslucentTangentsSkinned;
|
||||
QMap<QString, int> _unsortedMeshesTranslucentTangentsSpecularSkinned;
|
||||
QMap<QString, int> _unsortedMeshesTranslucentSpecularSkinned;
|
||||
|
||||
QMap<QString, int> _unsortedMeshesOpaque;
|
||||
QMap<QString, int> _unsortedMeshesOpaqueTangents;
|
||||
QMap<QString, int> _unsortedMeshesOpaqueTangentsSpecular;
|
||||
QMap<QString, int> _unsortedMeshesOpaqueSpecular;
|
||||
|
||||
QMap<QString, int> _unsortedMeshesOpaqueSkinned;
|
||||
QMap<QString, int> _unsortedMeshesOpaqueTangentsSkinned;
|
||||
QMap<QString, int> _unsortedMeshesOpaqueTangentsSpecularSkinned;
|
||||
QMap<QString, int> _unsortedMeshesOpaqueSpecularSkinned;
|
||||
|
||||
QVector<int> _meshesTranslucent;
|
||||
QVector<int> _meshesTranslucentTangents;
|
||||
QVector<int> _meshesTranslucentTangentsSpecular;
|
||||
QVector<int> _meshesTranslucentSpecular;
|
||||
|
||||
QVector<int> _meshesTranslucentSkinned;
|
||||
QVector<int> _meshesTranslucentTangentsSkinned;
|
||||
QVector<int> _meshesTranslucentTangentsSpecularSkinned;
|
||||
QVector<int> _meshesTranslucentSpecularSkinned;
|
||||
|
||||
QVector<int> _meshesOpaque;
|
||||
QVector<int> _meshesOpaqueTangents;
|
||||
QVector<int> _meshesOpaqueTangentsSpecular;
|
||||
QVector<int> _meshesOpaqueSpecular;
|
||||
|
||||
QVector<int> _meshesOpaqueSkinned;
|
||||
QVector<int> _meshesOpaqueTangentsSkinned;
|
||||
QVector<int> _meshesOpaqueTangentsSpecularSkinned;
|
||||
QVector<int> _meshesOpaqueSpecularSkinned;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QPointer<Model>)
|
||||
|
|
|
@ -210,7 +210,7 @@ QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
|
|||
|
||||
if (!_primaryFramebufferObject) {
|
||||
_primaryFramebufferObject = createFramebufferObject();
|
||||
|
||||
|
||||
glGenTextures(1, &_primaryDepthTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(),
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
|
||||
/// Sets the desired texture resolution for the framebuffer objects.
|
||||
void setFrameBufferSize(QSize frameBufferSize);
|
||||
const QSize& getFrameBufferSize() const { return _frameBufferSize; }
|
||||
|
||||
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
|
||||
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
|
||||
|
|
|
@ -22,6 +22,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting) {
|
|||
QSettings* settings = Application::getInstance()->lockSettings();
|
||||
QVariant value = settings->value(setting);
|
||||
Application::getInstance()->unlockSettings();
|
||||
if (!value.isValid()) {
|
||||
value = "";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -29,6 +32,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting, const QVar
|
|||
QSettings* settings = Application::getInstance()->lockSettings();
|
||||
QVariant value = settings->value(setting, defaultValue);
|
||||
Application::getInstance()->unlockSettings();
|
||||
if (!value.isValid()) {
|
||||
value = "";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1392,16 +1392,14 @@ void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3&
|
|||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
QColor color = _color->getColor();
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
|
||||
Cuboid* cuboid = new Cuboid();
|
||||
cuboid->setTranslation((maximum + minimum) * 0.5f);
|
||||
glm::vec3 vector = (maximum - minimum) * 0.5f;
|
||||
cuboid->setScale(vector.x);
|
||||
cuboid->setAspectY(vector.y / vector.x);
|
||||
cuboid->setAspectZ(vector.z / vector.x);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid), material, color)) };
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid),
|
||||
material, _color->getColor())) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
|
@ -1489,9 +1487,7 @@ void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, cons
|
|||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
QColor color = _color->getColor();
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material, color)) };
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material, _color->getColor())) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
//
|
||||
// OAuthWebViewHandler.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-05-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <qwebview.h>
|
||||
#include <qurlquery.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "OAuthWebViewHandler.h"
|
||||
|
||||
OAuthWebViewHandler& OAuthWebViewHandler::getInstance() {
|
||||
static OAuthWebViewHandler sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
OAuthWebViewHandler::OAuthWebViewHandler() :
|
||||
_activeWebView(NULL),
|
||||
_webViewRedisplayTimer(),
|
||||
_lastAuthorizationURL()
|
||||
{
|
||||
addHighFidelityRootCAToSSLConfig();
|
||||
}
|
||||
|
||||
const char HIGH_FIDELITY_CA[] = "-----BEGIN CERTIFICATE-----\n"
|
||||
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
|
||||
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
|
||||
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
|
||||
"YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n"
|
||||
"E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n"
|
||||
"MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n"
|
||||
"BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n"
|
||||
"MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n"
|
||||
"IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n"
|
||||
"AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n"
|
||||
"2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n"
|
||||
"wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n"
|
||||
"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n"
|
||||
"HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n"
|
||||
"EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n"
|
||||
"bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n"
|
||||
"b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n"
|
||||
"c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n"
|
||||
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
|
||||
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
|
||||
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() {
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
// add the High Fidelity root CA to the list of trusted CA certificates
|
||||
QByteArray highFidelityCACertificate(HIGH_FIDELITY_CA, sizeof(HIGH_FIDELITY_CA));
|
||||
sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate));
|
||||
|
||||
// set the modified configuration
|
||||
QSslConfiguration::setDefaultConfiguration(sslConfig);
|
||||
}
|
||||
|
||||
const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000;
|
||||
|
||||
void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) {
|
||||
if (!_activeWebView) {
|
||||
|
||||
if (!_lastAuthorizationURL.isEmpty()) {
|
||||
if (_lastAuthorizationURL.host() == authorizationURL.host()
|
||||
&& _webViewRedisplayTimer.elapsed() < WEB_VIEW_REDISPLAY_ELAPSED_MSECS) {
|
||||
// this would be re-displaying an OAuth dialog for the same auth URL inside of the redisplay ms
|
||||
// so return instead
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_lastAuthorizationURL = authorizationURL;
|
||||
|
||||
_activeWebView = new QWebView;
|
||||
|
||||
// keep the window on top and delete it when it closes
|
||||
_activeWebView->setWindowFlags(Qt::Sheet);
|
||||
_activeWebView->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString();
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
QUrl codedAuthorizationURL = authorizationURL;
|
||||
|
||||
// check if we have an access token for this host - if so we can bypass login by adding it to the URL
|
||||
if (accountManager.getAuthURL().host() == authorizationURL.host()
|
||||
&& accountManager.hasValidAccessToken()) {
|
||||
|
||||
const QString ACCESS_TOKEN_QUERY_STRING_KEY = "access_token";
|
||||
|
||||
QUrlQuery authQuery(codedAuthorizationURL);
|
||||
authQuery.addQueryItem(ACCESS_TOKEN_QUERY_STRING_KEY, accountManager.getAccountInfo().getAccessToken().token);
|
||||
|
||||
codedAuthorizationURL.setQuery(authQuery);
|
||||
}
|
||||
|
||||
connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged);
|
||||
|
||||
_activeWebView->load(codedAuthorizationURL);
|
||||
|
||||
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors,
|
||||
this, &OAuthWebViewHandler::handleSSLErrors);
|
||||
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::finished,
|
||||
this, &OAuthWebViewHandler::handleReplyFinished);
|
||||
connect(_activeWebView.data(), &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished);
|
||||
|
||||
// connect to the destroyed signal so after the web view closes we can start a timer
|
||||
connect(_activeWebView.data(), &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed);
|
||||
}
|
||||
}
|
||||
|
||||
void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList) {
|
||||
qDebug() << "SSL Errors:" << errorList;
|
||||
}
|
||||
|
||||
void OAuthWebViewHandler::handleLoadFinished(bool success) {
|
||||
if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) {
|
||||
qDebug() << "OAuth authorization code passed successfully to domain-server.";
|
||||
|
||||
// grab the UUID that is set as the state parameter in the auth URL
|
||||
// since that is our new session UUID
|
||||
QUrlQuery authQuery(_activeWebView->url());
|
||||
|
||||
const QString AUTH_STATE_QUERY_KEY = "state";
|
||||
NodeList::getInstance()->setSessionUUID(QUuid(authQuery.queryItemValue(AUTH_STATE_QUERY_KEY)));
|
||||
|
||||
_activeWebView->close();
|
||||
_activeWebView = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void OAuthWebViewHandler::handleReplyFinished(QNetworkReply* reply) {
|
||||
if (_activeWebView && reply->error() != QNetworkReply::NoError) {
|
||||
qDebug() << "Error loading" << reply->url() << "-" << reply->errorString();
|
||||
_activeWebView->close();
|
||||
}
|
||||
}
|
||||
|
||||
void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) {
|
||||
_webViewRedisplayTimer.restart();
|
||||
}
|
||||
|
||||
void OAuthWebViewHandler::handleURLChanged(const QUrl& url) {
|
||||
// check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler
|
||||
const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token=";
|
||||
QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING);
|
||||
|
||||
if (accessTokenRegex.indexIn(url.toString()) != -1) {
|
||||
_activeWebView->show();
|
||||
} else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") {
|
||||
// this is a login request - we're going to close the webview and signal the AccountManager that we need a login
|
||||
qDebug() << "data-server replied with login request. Signalling that login is required to proceed with OAuth.";
|
||||
_activeWebView->close();
|
||||
AccountManager::getInstance().checkAndSignalForAccessToken();
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// OAuthWebviewHandler.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-05-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_OAuthWebviewHandler_h
|
||||
#define hifi_OAuthWebviewHandler_h
|
||||
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
class QWebView;
|
||||
|
||||
class OAuthWebViewHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OAuthWebViewHandler();
|
||||
static OAuthWebViewHandler& getInstance();
|
||||
static void addHighFidelityRootCAToSSLConfig();
|
||||
|
||||
void clearLastAuthorizationURL() { _lastAuthorizationURL = QUrl(); }
|
||||
|
||||
public slots:
|
||||
void displayWebviewForAuthorizationURL(const QUrl& authorizationURL);
|
||||
|
||||
private slots:
|
||||
void handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList);
|
||||
void handleLoadFinished(bool success);
|
||||
void handleReplyFinished(QNetworkReply* reply);
|
||||
void handleWebViewDestroyed(QObject* destroyedObject);
|
||||
void handleURLChanged(const QUrl& url);
|
||||
private:
|
||||
QPointer<QWebView> _activeWebView;
|
||||
QElapsedTimer _webViewRedisplayTimer;
|
||||
QUrl _lastAuthorizationURL;
|
||||
};
|
||||
|
||||
#endif // hifi_OAuthWebviewHandler_h
|
|
@ -40,6 +40,9 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
|
|||
connect(&_proxyModel, &QSortFilterProxyModel::modelReset,
|
||||
this, &RunningScriptsWidget::selectFirstInList);
|
||||
|
||||
QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText);
|
||||
ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts.");
|
||||
|
||||
_proxyModel.setSourceModel(&_scriptsModel);
|
||||
_proxyModel.sort(0, Qt::AscendingOrder);
|
||||
_proxyModel.setDynamicSortFilter(true);
|
||||
|
@ -86,7 +89,7 @@ void RunningScriptsWidget::setBoundary(const QRect& rect) {
|
|||
void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
||||
setUpdatesEnabled(false);
|
||||
QLayoutItem* widget;
|
||||
while ((widget = ui->scrollAreaWidgetContents->layout()->takeAt(0)) != NULL) {
|
||||
while ((widget = ui->scriptListWidget->layout()->takeAt(0)) != NULL) {
|
||||
delete widget->widget();
|
||||
delete widget;
|
||||
}
|
||||
|
@ -96,7 +99,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
|||
if (!hash.contains(list.at(i))) {
|
||||
hash.insert(list.at(i), 1);
|
||||
}
|
||||
QWidget* row = new QWidget(ui->scrollAreaWidgetContents);
|
||||
QWidget* row = new QWidget(ui->scriptListWidget);
|
||||
row->setLayout(new QHBoxLayout(row));
|
||||
|
||||
QUrl url = QUrl(list.at(i));
|
||||
|
@ -130,17 +133,16 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
|||
line->setFrameShape(QFrame::HLine);
|
||||
line->setStyleSheet("color: #E1E1E1; margin-left: 6px; margin-right: 6px;");
|
||||
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(row);
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(line);
|
||||
ui->scriptListWidget->layout()->addWidget(row);
|
||||
ui->scriptListWidget->layout()->addWidget(line);
|
||||
}
|
||||
|
||||
|
||||
ui->noRunningScriptsLabel->setVisible(list.isEmpty());
|
||||
ui->runningScriptsList->setVisible(!list.isEmpty());
|
||||
ui->reloadAllButton->setVisible(!list.isEmpty());
|
||||
ui->stopAllButton->setVisible(!list.isEmpty());
|
||||
|
||||
ui->scrollAreaWidgetContents->updateGeometry();
|
||||
ui->scriptListWidget->updateGeometry();
|
||||
setUpdatesEnabled(true);
|
||||
Application::processEvents();
|
||||
repaint();
|
||||
|
|
|
@ -221,8 +221,30 @@ void Stats::display(
|
|||
int totalServers = NodeList::getInstance()->size();
|
||||
|
||||
lines = _expanded ? 5 : 3;
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _generalStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
int columnOneWidth = _generalStatsWidth;
|
||||
|
||||
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
|
||||
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
|
||||
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // make it 3 columns wide...
|
||||
// we will also include room for 1 line per timing record and a header of 4 lines
|
||||
lines += 4;
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
lines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
int columnOneHorizontalOffset = horizontalOffset;
|
||||
|
||||
char serverNodes[30];
|
||||
sprintf(serverNodes, "Servers: %d", totalServers);
|
||||
|
@ -249,6 +271,46 @@ void Stats::display(
|
|||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond, color);
|
||||
}
|
||||
|
||||
// TODO: the display of these timing details should all be moved to JavaScript
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
// Timing details...
|
||||
const int TIMER_OUTPUT_LINE_LENGTH = 1000;
|
||||
char perfLine[TIMER_OUTPUT_LINE_LENGTH];
|
||||
verticalOffset += STATS_PELS_PER_LINE * 4; // skip 4 lines to be under the other columns
|
||||
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font,
|
||||
"-------------------------------------------------------- Function "
|
||||
"------------------------------------------------------- --msecs- -calls--", color);
|
||||
|
||||
// First iterate all the records, and for the ones that should be included, insert them into
|
||||
// a new Map sorted by average time...
|
||||
QMap<float, QString> sortedRecords;
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC;
|
||||
sortedRecords.insertMulti(averageTime, i.key());
|
||||
}
|
||||
}
|
||||
|
||||
QMapIterator<float, QString> j(sortedRecords);
|
||||
j.toBack();
|
||||
while (j.hasPrevious()) {
|
||||
j.previous();
|
||||
QString functionName = j.value();
|
||||
const PerformanceTimerRecord& record = allRecords.value(functionName);
|
||||
|
||||
sprintf(perfLine, "%120s: %8.4f [%6llu]", qPrintable(functionName),
|
||||
(float)record.getMovingAverage() / (float)USECS_PER_MSEC,
|
||||
record.getCount());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine, color);
|
||||
}
|
||||
}
|
||||
|
||||
verticalOffset = 0;
|
||||
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1;
|
||||
|
@ -283,7 +345,11 @@ void Stats::display(
|
|||
}
|
||||
|
||||
lines = _expanded ? 4 : 3;
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
|
||||
// only draw our background if column one didn't draw a wide background
|
||||
if (columnOneWidth == _generalStatsWidth) {
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
}
|
||||
horizontalOffset += 5;
|
||||
|
||||
|
||||
|
@ -319,7 +385,9 @@ void Stats::display(
|
|||
|
||||
lines = _expanded ? 8 : 3;
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
if (columnOneWidth == _generalStatsWidth) {
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
}
|
||||
horizontalOffset += 5;
|
||||
|
||||
char avatarPosition[200];
|
||||
|
@ -391,39 +459,63 @@ void Stats::display(
|
|||
|
||||
VoxelSystem* voxels = Application::getInstance()->getVoxels();
|
||||
|
||||
lines = _expanded ? 11 : 3;
|
||||
lines = _expanded ? 14 : 3;
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
|
||||
lines += 9; // spatial audio processing adds 1 spacing line and 8 extra lines of info
|
||||
lines += 10; // spatial audio processing adds 1 spacing line and 8 extra lines of info
|
||||
}
|
||||
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
// we will also include room for 1 line per timing record and a header
|
||||
lines += 1;
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
lines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset,
|
||||
lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
// Model/Entity render details
|
||||
EntityTreeRenderer* entities = Application::getInstance()->getEntities();
|
||||
voxelStats.str("");
|
||||
voxelStats << "Entity Items rendered: " << entities->getItemsRendered()
|
||||
<< " / Out of view:" << entities->getItemsOutOfView()
|
||||
<< " / Too small:" << entities->getItemsTooSmall();
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
if (_expanded) {
|
||||
voxelStats.str("");
|
||||
voxelStats << " Meshes rendered: " << entities->getMeshesRendered()
|
||||
<< " / Out of view:" << entities->getMeshesOutOfView()
|
||||
<< " / Too small:" << entities->getMeshesTooSmall();
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats << " Triangles: " << entities->getTrianglesRendered()
|
||||
<< " / Quads:" << entities->getQuadsRendered()
|
||||
<< " / Material Switches:" << entities->getMaterialSwitches();
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats << " Mesh Parts Rendered Opaque: " << entities->getOpaqueMeshPartsRendered()
|
||||
<< " / Translucent:" << entities->getTranslucentMeshPartsRendered();
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats.precision(4);
|
||||
voxelStats << "Voxels Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " <<
|
||||
"Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K ";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
if (_expanded) {
|
||||
// Local Voxel Memory Usage
|
||||
voxelStats.str("");
|
||||
voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
|
||||
voxelStats << " Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB / " <<
|
||||
" Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB / " <<
|
||||
"VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB";
|
||||
if (voxels->hasVoxelMemoryUsageGPU()) {
|
||||
voxelStats << " / GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.f << "MB";
|
||||
|
@ -434,18 +526,11 @@ void Stats::display(
|
|||
// Voxel Rendering
|
||||
voxelStats.str("");
|
||||
voxelStats.precision(4);
|
||||
voxelStats << "Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K";
|
||||
voxelStats << " Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats.precision(4);
|
||||
voxelStats << "Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " <<
|
||||
"Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K ";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
|
||||
std::stringstream sendingMode("");
|
||||
sendingMode << "Octree Sending Mode: [";
|
||||
|
@ -516,44 +601,44 @@ void Stats::display(
|
|||
}
|
||||
|
||||
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
|
||||
unsigned long localTotal = VoxelTreeElement::getNodeCount();
|
||||
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
|
||||
|
||||
// Server Voxels
|
||||
voxelStats.str("");
|
||||
voxelStats << "Server voxels: " << qPrintable(serversTotalString);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
if (_expanded) {
|
||||
QString serversInternalString = locale.toString((uint)totalInternal);
|
||||
QString serversLeavesString = locale.toString((uint)totalLeaves);
|
||||
|
||||
// Server Octree Elements
|
||||
if (!_expanded) {
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Internal: " << qPrintable(serversInternalString) << " " <<
|
||||
"Leaves: " << qPrintable(serversLeavesString) << "";
|
||||
voxelStats << "Octree Elements Server: " << qPrintable(serversTotalString)
|
||||
<< " Local:" << qPrintable(localTotalString);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
unsigned long localTotal = VoxelTreeElement::getNodeCount();
|
||||
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
|
||||
|
||||
// Local Voxels
|
||||
voxelStats.str("");
|
||||
voxelStats << "Local voxels: " << qPrintable(localTotalString);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
if (_expanded) {
|
||||
voxelStats.str("");
|
||||
voxelStats << "Octree Elements -";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
QString serversInternalString = locale.toString((uint)totalInternal);
|
||||
QString serversLeavesString = locale.toString((uint)totalLeaves);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats << " Server: " << qPrintable(serversTotalString) <<
|
||||
" Internal: " << qPrintable(serversInternalString) <<
|
||||
" Leaves: " << qPrintable(serversLeavesString);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
|
||||
// Local Voxels
|
||||
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
|
||||
unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount();
|
||||
QString localInternalString = locale.toString((uint)localInternal);
|
||||
QString localLeavesString = locale.toString((uint)localLeaves);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Internal: " << qPrintable(localInternalString) << " " <<
|
||||
"Leaves: " << qPrintable(localLeavesString) << "";
|
||||
voxelStats << " Local: " << qPrintable(serversTotalString) <<
|
||||
" Internal: " << qPrintable(localInternalString) <<
|
||||
" Leaves: " << qPrintable(localLeavesString) << "";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
@ -567,32 +652,6 @@ void Stats::display(
|
|||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
PerformanceTimer::tallyAllTimerRecords();
|
||||
|
||||
// TODO: the display of these timing details should all be moved to JavaScript
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
// Timing details...
|
||||
const int TIMER_OUTPUT_LINE_LENGTH = 300;
|
||||
char perfLine[TIMER_OUTPUT_LINE_LENGTH];
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font,
|
||||
"--------------------- Function -------------------- --msecs- -calls--", color);
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
sprintf(perfLine, "%50s: %8.4f [%6llu]", qPrintable(i.key()),
|
||||
(float)i.value().getMovingAverage() / (float)USECS_PER_MSEC,
|
||||
i.value().getCount());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, perfLine, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
|
||||
verticalOffset += STATS_PELS_PER_LINE; // space one line...
|
||||
|
||||
|
|
|
@ -13,6 +13,5 @@
|
|||
#define hifi_world_h
|
||||
|
||||
const float GRAVITY_EARTH = 9.80665f;
|
||||
const float EDGE_SIZE_GROUND_PLANE = 20.f;
|
||||
|
||||
#endif // hifi_world_h
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>319</width>
|
||||
<height>481</height>
|
||||
<width>364</width>
|
||||
<height>728</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -16,19 +16,7 @@
|
|||
<property name="styleSheet">
|
||||
<string notr="true">* { font-family: Helvetica, Arial, sans-serif; }</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="3,2">
|
||||
<item>
|
||||
<widget class="QWidget" name="runningScriptsArea" native="true">
|
||||
<property name="sizePolicy">
|
||||
|
@ -195,6 +183,25 @@ font: bold 16px;
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="noRunningScriptsLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 14px; color: #5f5f5f; margin: 2px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>There are no scripts running.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="runningScriptsList">
|
||||
<property name="sizePolicy">
|
||||
|
@ -219,10 +226,10 @@ font: bold 16px;
|
|||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
|
@ -238,8 +245,8 @@ font: bold 16px;
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>264</width>
|
||||
<height>16</height>
|
||||
<width>328</width>
|
||||
<height>18</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -254,7 +261,7 @@ font: bold 16px;
|
|||
<property name="styleSheet">
|
||||
<string notr="true">font-size: 14px;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -270,29 +277,41 @@ font: bold 16px;
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="scriptListWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="tipLabel">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 14px; color: #5f5f5f; margin: 2px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="noRunningScriptsLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 14px; color: #5f5f5f;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>There are no scripts running.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -30,8 +30,7 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b
|
|||
_shouldLoopbackForNode(false),
|
||||
_isStereo(isStereo),
|
||||
_lastPopOutputTrailingLoudness(0.0f),
|
||||
_lastPopOutputLoudness(0.0f),
|
||||
_listenerUnattenuatedZone(NULL)
|
||||
_lastPopOutputLoudness(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,6 @@ public:
|
|||
PositionalAudioStream::Type getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
AABox* getListenerUnattenuatedZone() const { return _listenerUnattenuatedZone; }
|
||||
|
||||
void setListenerUnattenuatedZone(AABox* listenerUnattenuatedZone) { _listenerUnattenuatedZone = listenerUnattenuatedZone; }
|
||||
|
||||
protected:
|
||||
// disallow copying of PositionalAudioStream objects
|
||||
|
@ -63,7 +60,6 @@ protected:
|
|||
|
||||
float _lastPopOutputTrailingLoudness;
|
||||
float _lastPopOutputLoudness;
|
||||
AABox* _listenerUnattenuatedZone;
|
||||
};
|
||||
|
||||
#endif // hifi_PositionalAudioStream_h
|
||||
|
|
|
@ -659,6 +659,7 @@ public:
|
|||
glm::vec3 emissive;
|
||||
float shininess;
|
||||
float opacity;
|
||||
QString id;
|
||||
};
|
||||
|
||||
class Cluster {
|
||||
|
@ -1319,7 +1320,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
}
|
||||
}
|
||||
}
|
||||
materials.insert(getID(object.properties), material);
|
||||
material.id = getID(object.properties);
|
||||
materials.insert(material.id, material);
|
||||
|
||||
} else if (object.name == "Deformer") {
|
||||
if (object.properties.last() == "Cluster") {
|
||||
|
@ -1621,6 +1623,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
if (!specularTexture.filename.isNull()) {
|
||||
part.specularTexture = specularTexture;
|
||||
}
|
||||
part.materialID = material.id;
|
||||
}
|
||||
}
|
||||
materialIndex++;
|
||||
|
|
|
@ -115,6 +115,8 @@ public:
|
|||
FBXTexture diffuseTexture;
|
||||
FBXTexture normalTexture;
|
||||
FBXTexture specularTexture;
|
||||
|
||||
QString materialID;
|
||||
};
|
||||
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
|
|
|
@ -74,6 +74,11 @@ void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool
|
|||
applyEdit(edit, reliable);
|
||||
}
|
||||
|
||||
void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) {
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldHeightEdit(position, radius, height)) };
|
||||
applyEdit(edit, true);
|
||||
}
|
||||
|
||||
void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
|
||||
QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable));
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ public:
|
|||
|
||||
Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false);
|
||||
|
||||
Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height);
|
||||
|
||||
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
|
||||
|
||||
/// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread.
|
||||
|
|
|
@ -18,6 +18,10 @@ void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash
|
|||
MetavoxelEdit::~MetavoxelEdit() {
|
||||
}
|
||||
|
||||
void MetavoxelEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
BoxSetEdit::BoxSetEdit(const Box& region, float granularity, const OwnedAttributeValue& value) :
|
||||
region(region), granularity(granularity), value(value) {
|
||||
}
|
||||
|
@ -408,6 +412,11 @@ void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObje
|
|||
data.guide(visitor);
|
||||
}
|
||||
|
||||
MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
material(material),
|
||||
averageColor(averageColor) {
|
||||
}
|
||||
|
||||
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
|
@ -595,10 +604,9 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
|||
|
||||
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
MaterialEdit(material, averageColor),
|
||||
position(position),
|
||||
radius(radius),
|
||||
material(material),
|
||||
averageColor(averageColor) {
|
||||
radius(radius) {
|
||||
}
|
||||
|
||||
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
|
@ -613,9 +621,8 @@ const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES;
|
|||
|
||||
VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
spanner(spanner),
|
||||
material(material),
|
||||
averageColor(averageColor) {
|
||||
MaterialEdit(material, averageColor),
|
||||
spanner(spanner) {
|
||||
}
|
||||
|
||||
class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor {
|
||||
|
@ -845,16 +852,18 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject
|
|||
while (!data.getBounds().contains(spanner->getBounds())) {
|
||||
data.expand();
|
||||
}
|
||||
VoxelMaterialSpannerEditVisitor visitor(spanner, material, averageColor);
|
||||
// make sure it's either 100% transparent or 100% opaque
|
||||
QColor color = averageColor;
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
VoxelMaterialSpannerEditVisitor visitor(spanner, material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
MaterialEdit(material, averageColor),
|
||||
position(position),
|
||||
radius(radius),
|
||||
material(material),
|
||||
averageColor(averageColor) {
|
||||
radius(radius) {
|
||||
}
|
||||
|
||||
class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor {
|
||||
|
@ -950,6 +959,9 @@ int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
PaintVoxelMaterialEditVisitor visitor(position, radius, material, averageColor);
|
||||
// make sure it's 100% opaque
|
||||
QColor color = averageColor;
|
||||
color.setAlphaF(1.0f);
|
||||
PaintVoxelMaterialEditVisitor visitor(position, radius, material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public:
|
|||
|
||||
virtual ~MetavoxelEdit();
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const = 0;
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
||||
/// An edit that sets the region within a box to a value.
|
||||
|
@ -224,16 +224,28 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldHeightEdit)
|
||||
|
||||
/// Base class for edits that have materials.
|
||||
class MaterialEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM SharedObjectPointer material;
|
||||
STREAM QColor averageColor;
|
||||
|
||||
MaterialEdit(const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(MaterialEdit)
|
||||
|
||||
/// An edit that sets a region of a heightfield material.
|
||||
class PaintHeightfieldMaterialEdit : public MetavoxelEdit {
|
||||
class PaintHeightfieldMaterialEdit : STREAM public MaterialEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
STREAM SharedObjectPointer material;
|
||||
STREAM QColor averageColor;
|
||||
|
||||
PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
|
@ -244,14 +256,12 @@ public:
|
|||
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit)
|
||||
|
||||
/// An edit that sets the materials of voxels within a spanner to a value.
|
||||
class VoxelMaterialSpannerEdit : public MetavoxelEdit {
|
||||
class VoxelMaterialSpannerEdit : STREAM public MaterialEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM SharedObjectPointer spanner;
|
||||
STREAM SharedObjectPointer material;
|
||||
STREAM QColor averageColor;
|
||||
|
||||
VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
|
@ -262,15 +272,13 @@ public:
|
|||
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit)
|
||||
|
||||
/// An edit that sets a region of a voxel material.
|
||||
class PaintVoxelMaterialEdit : public MetavoxelEdit {
|
||||
class PaintVoxelMaterialEdit : STREAM public MaterialEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
STREAM SharedObjectPointer material;
|
||||
STREAM QColor averageColor;
|
||||
|
||||
PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
|
|
|
@ -10,5 +10,19 @@ if (WIN32)
|
|||
target_link_libraries(${TARGET_NAME} ws2_32.lib)
|
||||
endif ()
|
||||
|
||||
# find OpenSSL
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
|
||||
# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto
|
||||
message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings."
|
||||
"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.")
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
# append OpenSSL to our list of libraries to link
|
||||
list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}")
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
|
@ -19,9 +19,11 @@
|
|||
#include <QtCore/QUrlQuery>
|
||||
#include <QtNetwork/QHttpMultiPart>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <qthread.h>
|
||||
|
||||
#include "NodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "RSAKeypairGenerator.h"
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
||||
|
@ -144,6 +146,12 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
|
|||
} else {
|
||||
requestProfile();
|
||||
}
|
||||
|
||||
// if we don't have a private key in settings we should generate a new keypair
|
||||
if (!_accountInfo.hasPrivateKey()) {
|
||||
qDebug() << "No private key present - generating a new key-pair.";
|
||||
generateNewKeypair();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -280,14 +288,12 @@ void AccountManager::processReply() {
|
|||
}
|
||||
|
||||
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
|
||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||
|
||||
if (callbackParams.jsonCallbackReceiver) {
|
||||
// invoke the right method on the callback receiver
|
||||
QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod),
|
||||
Q_ARG(const QJsonObject&, jsonResponse.object()));
|
||||
Q_ARG(QNetworkReply&, *requestReply));
|
||||
|
||||
// remove the related reply-callback group from the map
|
||||
_pendingCallbackMap.remove(requestReply);
|
||||
|
@ -295,7 +301,7 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
|||
} else {
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "Received JSON response from data-server that has no matching callback.";
|
||||
qDebug() << jsonResponse;
|
||||
qDebug() << QJsonDocument::fromJson(requestReply->readAll());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -319,6 +325,16 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
|||
}
|
||||
}
|
||||
|
||||
void AccountManager::persistAccountToSettings() {
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
}
|
||||
|
||||
bool AccountManager::hasValidAccessToken() {
|
||||
|
||||
if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
|
||||
|
@ -408,15 +424,10 @@ void AccountManager::requestAccessTokenFinished() {
|
|||
|
||||
emit loginComplete(rootURL);
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
persistAccountToSettings();
|
||||
|
||||
requestProfile();
|
||||
generateNewKeypair();
|
||||
}
|
||||
} else {
|
||||
// TODO: error handling
|
||||
|
@ -434,7 +445,7 @@ void AccountManager::requestProfile() {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QUrl profileURL = _authURL;
|
||||
profileURL.setPath("/api/v1/users/profile");
|
||||
profileURL.setPath("/api/v1/user/profile");
|
||||
|
||||
QNetworkRequest profileRequest(profileURL);
|
||||
profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue());
|
||||
|
@ -458,15 +469,8 @@ void AccountManager::requestProfileFinished() {
|
|||
// the username has changed to whatever came back
|
||||
emit usernameChanged(_accountInfo.getUsername());
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store the whole profile into the local settings
|
||||
QUrl rootURL = profileReply->url();
|
||||
rootURL.setPath("");
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
// store the whole profile into the local settings
|
||||
persistAccountToSettings();
|
||||
|
||||
} else {
|
||||
// TODO: error handling
|
||||
|
@ -478,3 +482,57 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) {
|
|||
// TODO: error handling
|
||||
qDebug() << "AccountManager requestProfileError - " << error;
|
||||
}
|
||||
|
||||
void AccountManager::generateNewKeypair() {
|
||||
// setup a new QThread to generate the keypair on, in case it takes a while
|
||||
QThread* generateThread = new QThread(this);
|
||||
|
||||
// setup a keypair generator
|
||||
RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator();
|
||||
|
||||
connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair);
|
||||
connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair);
|
||||
connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair,
|
||||
this, &AccountManager::handleKeypairGenerationError);
|
||||
connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit);
|
||||
connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater);
|
||||
|
||||
keypairGenerator->moveToThread(generateThread);
|
||||
|
||||
qDebug() << "Starting worker thread to generate 2048-bit RSA key-pair.";
|
||||
generateThread->start();
|
||||
}
|
||||
|
||||
void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey) {
|
||||
|
||||
qDebug() << "Generated 2048-bit RSA key-pair. Storing private key and uploading public key.";
|
||||
|
||||
// set the private key on our data-server account info
|
||||
_accountInfo.setPrivateKey(privateKey);
|
||||
persistAccountToSettings();
|
||||
|
||||
// upload the public key so data-web has an up-to-date key
|
||||
const QString PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
|
||||
|
||||
// setup a multipart upload to send up the public key
|
||||
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart keyPart;
|
||||
keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
keyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
|
||||
keyPart.setBody(publicKey);
|
||||
|
||||
requestMultiPart->append(keyPart);
|
||||
|
||||
authenticatedRequest(PUBLIC_KEY_UPDATE_PATH, QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), QByteArray(), requestMultiPart);
|
||||
|
||||
// get rid of the keypair generator now that we don't need it anymore
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void AccountManager::handleKeypairGenerationError() {
|
||||
// for now there isn't anything we do with this except get the worker thread to clean up
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public:
|
|||
void requestAccessToken(const QString& login, const QString& password);
|
||||
void requestProfile();
|
||||
|
||||
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
|
||||
DataServerAccountInfo& getAccountInfo() { return _accountInfo; }
|
||||
|
||||
public slots:
|
||||
void requestAccessTokenFinished();
|
||||
|
@ -91,13 +91,19 @@ signals:
|
|||
void balanceChanged(qint64 newBalance);
|
||||
private slots:
|
||||
void processReply();
|
||||
void handleKeypairGenerationError();
|
||||
void processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey);
|
||||
private:
|
||||
AccountManager();
|
||||
AccountManager(AccountManager const& other); // not implemented
|
||||
void operator=(AccountManager const& other); // not implemented
|
||||
|
||||
void persistAccountToSettings();
|
||||
|
||||
void passSuccessToCallback(QNetworkReply* reply);
|
||||
void passErrorToCallback(QNetworkReply* reply);
|
||||
|
||||
void generateNewKeypair();
|
||||
|
||||
Q_INVOKABLE void invokedRequest(const QString& path,
|
||||
bool requiresAuthentication,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <qdebug.h>
|
||||
#include <qjsondocument.h>
|
||||
#include <qregexp.h>
|
||||
#include <qstringlist.h>
|
||||
|
||||
|
@ -134,8 +135,9 @@ void AddressManager::handleLookupString(const QString& lookupString) {
|
|||
}
|
||||
}
|
||||
|
||||
void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) {
|
||||
QJsonObject dataObject = jsonObject["data"].toObject();
|
||||
void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
|
||||
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||
QJsonObject dataObject = responseObject["data"].toObject();
|
||||
|
||||
const QString ADDRESS_API_DOMAIN_KEY = "domain";
|
||||
const QString ADDRESS_API_ONLINE_KEY = "online";
|
||||
|
@ -190,7 +192,7 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) {
|
|||
|
||||
} else {
|
||||
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
|
||||
qDebug() << jsonObject;
|
||||
qDebug() << responseObject;
|
||||
}
|
||||
} else {
|
||||
// we've been told that this result exists but is offline, emit our signal so the application can handle
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
public slots:
|
||||
void handleLookupString(const QString& lookupString);
|
||||
|
||||
void handleAPIResponse(const QJsonObject& jsonObject);
|
||||
void handleAPIResponse(QNetworkReply& requestReply);
|
||||
void handleAPIError(QNetworkReply& errorReply);
|
||||
void goToUser(const QString& username);
|
||||
signals:
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include <qjsondocument.h>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "DataServerAccountInfo.h"
|
||||
|
@ -20,7 +23,9 @@ DataServerAccountInfo::DataServerAccountInfo() :
|
|||
_discourseApiKey(),
|
||||
_walletID(),
|
||||
_balance(0),
|
||||
_hasBalance(false)
|
||||
_hasBalance(false),
|
||||
_privateKey(),
|
||||
_usernameSignature()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -33,6 +38,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI
|
|||
_walletID = otherInfo._walletID;
|
||||
_balance = otherInfo._balance;
|
||||
_hasBalance = otherInfo._hasBalance;
|
||||
_privateKey = otherInfo._privateKey;
|
||||
}
|
||||
|
||||
DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
|
||||
|
@ -51,6 +57,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
|||
swap(_walletID, otherInfo._walletID);
|
||||
swap(_balance, otherInfo._balance);
|
||||
swap(_hasBalance, otherInfo._hasBalance);
|
||||
swap(_privateKey, otherInfo._privateKey);
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) {
|
||||
|
@ -61,6 +68,9 @@ void DataServerAccountInfo::setUsername(const QString& username) {
|
|||
if (_username != username) {
|
||||
_username = username;
|
||||
|
||||
// clear our username signature so it has to be re-created
|
||||
_usernameSignature = QByteArray();
|
||||
|
||||
qDebug() << "Username changed to" << username;
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +102,8 @@ void DataServerAccountInfo::setBalance(qint64 balance) {
|
|||
}
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) {
|
||||
void DataServerAccountInfo::setBalanceFromJSON(QNetworkReply& requestReply) {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble();
|
||||
setBalance(balanceInSatoshis);
|
||||
|
@ -111,12 +122,55 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject
|
|||
setWalletID(QUuid(user["wallet_id"].toString()));
|
||||
}
|
||||
|
||||
const QByteArray& DataServerAccountInfo::getUsernameSignature() {
|
||||
if (_usernameSignature.isEmpty()) {
|
||||
if (!_privateKey.isEmpty()) {
|
||||
const char* privateKeyData = _privateKey.constData();
|
||||
RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL,
|
||||
reinterpret_cast<const unsigned char**>(&privateKeyData),
|
||||
_privateKey.size());
|
||||
if (rsaPrivateKey) {
|
||||
QByteArray usernameByteArray = _username.toUtf8();
|
||||
_usernameSignature.resize(RSA_size(rsaPrivateKey));
|
||||
|
||||
int encryptReturn = RSA_private_encrypt(usernameByteArray.size(),
|
||||
reinterpret_cast<const unsigned char*>(usernameByteArray.constData()),
|
||||
reinterpret_cast<unsigned char*>(_usernameSignature.data()),
|
||||
rsaPrivateKey, RSA_PKCS1_PADDING);
|
||||
|
||||
if (encryptReturn == -1) {
|
||||
qDebug() << "Error encrypting username signature.";
|
||||
qDebug() << "Will re-attempt on next domain-server check in.";
|
||||
_usernameSignature = QByteArray();
|
||||
}
|
||||
|
||||
// free the private key RSA struct now that we are done with it
|
||||
RSA_free(rsaPrivateKey);
|
||||
} else {
|
||||
qDebug() << "Could not create RSA struct from QByteArray private key.";
|
||||
qDebug() << "Will re-attempt on next domain-server check in.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _usernameSignature;
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) {
|
||||
_privateKey = privateKey;
|
||||
|
||||
// clear our username signature so it has to be re-created
|
||||
_usernameSignature = QByteArray();
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
|
||||
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._walletID;
|
||||
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey
|
||||
<< info._walletID << info._privateKey;
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) {
|
||||
in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._walletID;
|
||||
in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey
|
||||
>> info._walletID >> info._privateKey;
|
||||
return in;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_DataServerAccountInfo_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <qnetworkreply.h>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "OAuthAccessToken.h"
|
||||
|
@ -41,13 +42,17 @@ public:
|
|||
|
||||
const QUuid& getWalletID() const { return _walletID; }
|
||||
void setWalletID(const QUuid& walletID);
|
||||
|
||||
const QByteArray& getUsernameSignature();
|
||||
bool hasPrivateKey() const { return !_privateKey.isEmpty(); }
|
||||
void setPrivateKey(const QByteArray& privateKey);
|
||||
|
||||
qint64 getBalance() const { return _balance; }
|
||||
float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; }
|
||||
void setBalance(qint64 balance);
|
||||
bool hasBalance() const { return _hasBalance; }
|
||||
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
|
||||
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
|
||||
Q_INVOKABLE void setBalanceFromJSON(QNetworkReply& requestReply);
|
||||
|
||||
bool hasProfile() const;
|
||||
|
||||
|
@ -67,6 +72,8 @@ private:
|
|||
QUuid _walletID;
|
||||
qint64 _balance;
|
||||
bool _hasBalance;
|
||||
QByteArray _privateKey;
|
||||
QByteArray _usernameSignature;
|
||||
};
|
||||
|
||||
#endif // hifi_DataServerAccountInfo_h
|
||||
|
|
|
@ -679,7 +679,6 @@ void LimitedNodeList::updateLocalSockAddr() {
|
|||
qDebug() << "Local socket has changed from" << _localSockAddr << "to" << newSockAddr;
|
||||
}
|
||||
|
||||
|
||||
_localSockAddr = newSockAddr;
|
||||
|
||||
emit localSockAddrChanged(_localSockAddr);
|
||||
|
|
|
@ -101,6 +101,8 @@ public:
|
|||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
SharedNodePointer updateSocketsForNode(const QUuid& uuid,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
|
||||
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
|
||||
|
||||
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
|
||||
void processKillNode(const QByteArray& datagram);
|
||||
|
|
|
@ -302,11 +302,20 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||
|
||||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr << _localSockAddr << (quint8) _nodeTypesOfInterest.size();
|
||||
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
|
||||
packetStream << nodeTypeOfInterest;
|
||||
|
||||
// if this is a connect request, and we can present a username signature, send it along
|
||||
if (!_domainHandler.isConnected()) {
|
||||
DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
|
||||
packetStream << accountInfo.getUsername();
|
||||
|
||||
const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature();
|
||||
|
||||
if (!usernameSignature.isEmpty()) {
|
||||
qDebug() << "Including username signature in domain connect request.";
|
||||
packetStream << usernameSignature;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUsingDTLS) {
|
||||
|
|
|
@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 6;
|
||||
return 7;
|
||||
case PacketTypeVoxelData:
|
||||
return VERSION_VOXELS_HAS_FILE_BREAKS;
|
||||
default:
|
||||
|
@ -110,7 +110,7 @@ QString nameForPacketType(PacketType type) {
|
|||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainOAuthRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectionDenied);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm);
|
||||
|
|
|
@ -38,7 +38,7 @@ enum PacketType {
|
|||
PacketTypeDomainListRequest,
|
||||
PacketTypeRequestAssignment,
|
||||
PacketTypeCreateAssignment,
|
||||
PacketTypeDomainOAuthRequest,
|
||||
PacketTypeDomainConnectionDenied,
|
||||
PacketTypeMuteEnvironment,
|
||||
PacketTypeAudioStreamStats,
|
||||
PacketTypeDataServerConfirm,
|
||||
|
@ -81,7 +81,7 @@ typedef char PacketVersion;
|
|||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeEntityQuery
|
||||
<< PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeEntityEditNack
|
||||
|
|
91
libraries/networking/src/RSAKeypairGenerator.cpp
Normal file
91
libraries/networking/src/RSAKeypairGenerator.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// RSAKeypairGenerator.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-10-14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include <qdebug.h>
|
||||
|
||||
#include "RSAKeypairGenerator.h"
|
||||
|
||||
RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RSAKeypairGenerator::generateKeypair() {
|
||||
|
||||
RSA* keyPair = RSA_new();
|
||||
BIGNUM* exponent = BN_new();
|
||||
|
||||
const unsigned long RSA_KEY_EXPONENT = 65537;
|
||||
BN_set_word(exponent, RSA_KEY_EXPONENT);
|
||||
|
||||
// seed the random number generator before we call RSA_generate_key_ex
|
||||
srand(time(NULL));
|
||||
|
||||
const int RSA_KEY_BITS = 2048;
|
||||
|
||||
if (!RSA_generate_key_ex(keyPair, RSA_KEY_BITS, exponent, NULL)) {
|
||||
qDebug() << "Error generating 2048-bit RSA Keypair -" << ERR_get_error();
|
||||
|
||||
emit errorGeneratingKeypair();
|
||||
|
||||
// we're going to bust out of here but first we cleanup the BIGNUM
|
||||
BN_free(exponent);
|
||||
return;
|
||||
}
|
||||
|
||||
// we don't need the BIGNUM anymore so clean that up
|
||||
BN_free(exponent);
|
||||
|
||||
// grab the public key and private key from the file
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_RSAPublicKey(keyPair, &publicKeyDER);
|
||||
|
||||
unsigned char* privateKeyDER = NULL;
|
||||
int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER);
|
||||
|
||||
if (publicKeyLength <= 0 || privateKeyLength <= 0) {
|
||||
qDebug() << "Error getting DER public or private key from RSA struct -" << ERR_get_error();
|
||||
|
||||
emit errorGeneratingKeypair();
|
||||
|
||||
// cleanup the RSA struct
|
||||
RSA_free(keyPair);
|
||||
|
||||
// cleanup the public and private key DER data, if required
|
||||
if (publicKeyLength > 0) {
|
||||
OPENSSL_free(publicKeyDER);
|
||||
}
|
||||
|
||||
if (privateKeyLength > 0) {
|
||||
OPENSSL_free(privateKeyDER);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// we have the public key and private key in memory
|
||||
// we can cleanup the RSA struct before we continue on
|
||||
RSA_free(keyPair);
|
||||
|
||||
QByteArray publicKeyArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength);
|
||||
QByteArray privateKeyArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength);
|
||||
|
||||
// cleanup the publicKeyDER and publicKeyDER data
|
||||
OPENSSL_free(publicKeyDER);
|
||||
OPENSSL_free(privateKeyDER);
|
||||
|
||||
emit generatedKeypair(publicKeyArray, privateKeyArray);
|
||||
}
|
28
libraries/networking/src/RSAKeypairGenerator.h
Normal file
28
libraries/networking/src/RSAKeypairGenerator.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// RSAKeypairGenerator.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-10-14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RSAKeypairGenerator_h
|
||||
#define hifi_RSAKeypairGenerator_h
|
||||
|
||||
#include <qobject.h>
|
||||
|
||||
class RSAKeypairGenerator : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RSAKeypairGenerator(QObject* parent = 0);
|
||||
public slots:
|
||||
void generateKeypair();
|
||||
signals:
|
||||
void errorGeneratingKeypair();
|
||||
void generatedKeypair(const QByteArray& publicKey, const QByteArray& privateKey);
|
||||
};
|
||||
|
||||
#endif // hifi_RSAKeypairGenerator_h
|
|
@ -69,7 +69,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
|
|||
multipart);
|
||||
}
|
||||
|
||||
void UserActivityLogger::requestFinished(const QJsonObject& object) {
|
||||
void UserActivityLogger::requestFinished(QNetworkReply& requestReply) {
|
||||
// qDebug() << object;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ public slots:
|
|||
void wentTo(QString destinationType, QString destinationName);
|
||||
|
||||
private slots:
|
||||
void requestFinished(const QJsonObject& object);
|
||||
void requestFinished(QNetworkReply& requestReply);
|
||||
void requestError(QNetworkReply& errorReply);
|
||||
|
||||
private:
|
||||
|
|
|
@ -162,12 +162,30 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) {
|
|||
}
|
||||
|
||||
void OctreeRenderer::render(RenderMode renderMode) {
|
||||
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, 0, 0, 0 };
|
||||
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
if (_tree) {
|
||||
_tree->lockForRead();
|
||||
_tree->recurseTreeWithOperation(renderOperation, &args);
|
||||
_tree->unlock();
|
||||
}
|
||||
_meshesConsidered = args._meshesConsidered;
|
||||
_meshesRendered = args._meshesRendered;
|
||||
_meshesOutOfView = args._meshesOutOfView;
|
||||
_meshesTooSmall = args._meshesTooSmall;
|
||||
|
||||
_elementsTouched = args._elementsTouched;
|
||||
_itemsRendered = args._itemsRendered;
|
||||
_itemsOutOfView = args._itemsOutOfView;
|
||||
_itemsTooSmall = args._itemsTooSmall;
|
||||
|
||||
_materialSwitches = args._materialSwitches;
|
||||
_trianglesRendered = args._trianglesRendered;
|
||||
_quadsRendered = args._quadsRendered;
|
||||
|
||||
_translucentMeshPartsRendered = args._translucentMeshPartsRendered;
|
||||
_opaqueMeshPartsRendered = args._opaqueMeshPartsRendered;
|
||||
|
||||
}
|
||||
|
||||
void OctreeRenderer::clear() {
|
||||
|
|
|
@ -63,10 +63,45 @@ public:
|
|||
|
||||
/// clears the tree
|
||||
virtual void clear();
|
||||
|
||||
int getElementsTouched() const { return _elementsTouched; }
|
||||
int getItemsRendered() const { return _itemsRendered; }
|
||||
int getItemsOutOfView() const { return _itemsOutOfView; }
|
||||
int getItemsTooSmall() const { return _itemsTooSmall; }
|
||||
|
||||
int getMeshesConsidered() const { return _meshesConsidered; }
|
||||
int getMeshesRendered() const { return _meshesRendered; }
|
||||
int getMeshesOutOfView() const { return _meshesOutOfView; }
|
||||
int getMeshesTooSmall() const { return _meshesTooSmall; }
|
||||
|
||||
int getMaterialSwitches() const { return _materialSwitches; }
|
||||
int getTrianglesRendered() const { return _trianglesRendered; }
|
||||
int getQuadsRendered() const { return _quadsRendered; }
|
||||
|
||||
int getTranslucentMeshPartsRendered() const { return _translucentMeshPartsRendered; }
|
||||
int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; }
|
||||
|
||||
protected:
|
||||
Octree* _tree;
|
||||
bool _managedTree;
|
||||
ViewFrustum* _viewFrustum;
|
||||
|
||||
int _elementsTouched;
|
||||
int _itemsRendered;
|
||||
int _itemsOutOfView;
|
||||
int _itemsTooSmall;
|
||||
|
||||
int _meshesConsidered;
|
||||
int _meshesRendered;
|
||||
int _meshesOutOfView;
|
||||
int _meshesTooSmall;
|
||||
|
||||
int _materialSwitches;
|
||||
int _trianglesRendered;
|
||||
int _quadsRendered;
|
||||
|
||||
int _translucentMeshPartsRendered;
|
||||
int _opaqueMeshPartsRendered;
|
||||
};
|
||||
|
||||
class RenderArgs {
|
||||
|
@ -80,6 +115,19 @@ public:
|
|||
int _elementsTouched;
|
||||
int _itemsRendered;
|
||||
int _itemsOutOfView;
|
||||
int _itemsTooSmall;
|
||||
|
||||
int _meshesConsidered;
|
||||
int _meshesRendered;
|
||||
int _meshesOutOfView;
|
||||
int _meshesTooSmall;
|
||||
|
||||
int _materialSwitches;
|
||||
int _trianglesRendered;
|
||||
int _quadsRendered;
|
||||
|
||||
int _translucentMeshPartsRendered;
|
||||
int _opaqueMeshPartsRendered;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -876,3 +876,10 @@ void ViewFrustum::getFurthestPointFromCameraVoxelScale(const AACube& box, glm::v
|
|||
}
|
||||
}
|
||||
|
||||
float ViewFrustum::distanceToCamera(const glm::vec3& point) const {
|
||||
glm::vec3 temp = getPosition() - point;
|
||||
float distanceToPoint = sqrtf(glm::dot(temp, temp));
|
||||
return distanceToPoint;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -118,6 +118,8 @@ public:
|
|||
|
||||
// assumes box is in voxel scale, not TREE_SCALE, will scale view frustum's position accordingly
|
||||
void getFurthestPointFromCameraVoxelScale(const AACube& box, glm::vec3& furthestPoint) const;
|
||||
|
||||
float distanceToCamera(const glm::vec3& point) const;
|
||||
|
||||
private:
|
||||
// Used for keyhole calculations
|
||||
|
|
|
@ -88,13 +88,11 @@ inline bool operator==(const AABox& a, const AABox& b) {
|
|||
}
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const AABox& box) {
|
||||
const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe
|
||||
|
||||
debug << "AABox[ ("
|
||||
<< box.getCorner().x * (float)TREE_SCALE << "," << box.getCorner().y * (float)TREE_SCALE << "," << box.getCorner().z * (float)TREE_SCALE << " ) to ("
|
||||
<< box.calcTopFarLeft().x * (float)TREE_SCALE << "," << box.calcTopFarLeft().y * (float)TREE_SCALE << "," << box.calcTopFarLeft().z * (float)TREE_SCALE << ") size: ("
|
||||
<< box.getDimensions().x * (float)TREE_SCALE << "," << box.getDimensions().y * (float)TREE_SCALE << "," << box.getDimensions().z * (float)TREE_SCALE << ")"
|
||||
<< " in meters]";
|
||||
<< box.getCorner().x << "," << box.getCorner().y << "," << box.getCorner().z << " ) to ("
|
||||
<< box.calcTopFarLeft().x << "," << box.calcTopFarLeft().y << "," << box.calcTopFarLeft().z << ") size: ("
|
||||
<< box.getDimensions().x << "," << box.getDimensions().y << "," << box.getDimensions().z << ")"
|
||||
<< "]";
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
|
|
@ -79,12 +79,11 @@ inline bool operator==(const AACube& a, const AACube& b) {
|
|||
}
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const AACube& cube) {
|
||||
const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe
|
||||
debug << "AACube[ ("
|
||||
<< cube.getCorner().x * (float)TREE_SCALE << "," << cube.getCorner().y * (float)TREE_SCALE << "," << cube.getCorner().z * (float)TREE_SCALE << " ) to ("
|
||||
<< cube.calcTopFarLeft().x * (float)TREE_SCALE << "," << cube.calcTopFarLeft().y * (float)TREE_SCALE << "," << cube.calcTopFarLeft().z * (float)TREE_SCALE << ") size: ("
|
||||
<< cube.getDimensions().x * (float)TREE_SCALE << "," << cube.getDimensions().y * (float)TREE_SCALE << "," << cube.getDimensions().z * (float)TREE_SCALE << ")"
|
||||
<< " in meters]";
|
||||
<< cube.getCorner().x << "," << cube.getCorner().y << "," << cube.getCorner().z << " ) to ("
|
||||
<< cube.calcTopFarLeft().x << "," << cube.calcTopFarLeft().y << "," << cube.calcTopFarLeft().z << ") size: ("
|
||||
<< cube.getDimensions().x << "," << cube.getDimensions().y << "," << cube.getDimensions().z << ")"
|
||||
<< "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,26 +129,31 @@ template<typename T> inline void ByteCountCoded<T>::decode(const QByteArray& fro
|
|||
|
||||
// next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray)
|
||||
int encodedByteCount = 0;
|
||||
int leadBits = 1;
|
||||
int bitAt;
|
||||
for (bitAt = 0; bitAt < bitCount; bitAt++) {
|
||||
if (encodedBits.at(bitAt)) {
|
||||
encodedByteCount++;
|
||||
leadBits++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
encodedByteCount++; // always at least one byte
|
||||
int expectedBitCount = encodedByteCount * BITS_IN_BYTE;
|
||||
|
||||
// Now, keep reading...
|
||||
int valueStartsAt = bitAt + 1;
|
||||
|
||||
T value = 0;
|
||||
T bitValue = 1;
|
||||
for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) {
|
||||
if(encodedBits.at(bitAt)) {
|
||||
value += bitValue;
|
||||
|
||||
if (expectedBitCount <= (encodedBits.size() - leadBits)) {
|
||||
// Now, keep reading...
|
||||
int valueStartsAt = bitAt + 1;
|
||||
T bitValue = 1;
|
||||
for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) {
|
||||
if(encodedBits.at(bitAt)) {
|
||||
value += bitValue;
|
||||
}
|
||||
bitValue *= 2;
|
||||
}
|
||||
bitValue *= 2;
|
||||
}
|
||||
data = value;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <QDebug>
|
||||
#include "StreamUtils.h"
|
||||
|
||||
class Extents {
|
||||
public:
|
||||
/// set minimum and maximum to FLT_MAX and -FLT_MAX respectively
|
||||
|
@ -54,4 +57,15 @@ public:
|
|||
glm::vec3 maximum;
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const Extents& extents) {
|
||||
debug << "Extents[ ("
|
||||
<< extents.minimum << " ) to ("
|
||||
<< extents.maximum << ") size: ("
|
||||
<< (extents.maximum - extents.minimum) << ")"
|
||||
<< " ]";
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
||||
|
||||
#endif // hifi_Extents_h
|
|
@ -214,10 +214,12 @@ template<typename Enum> inline void PropertyFlags<Enum>::decode(const QByteArray
|
|||
|
||||
// next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray)
|
||||
int encodedByteCount = 0;
|
||||
int leadBits = 1;
|
||||
int bitAt;
|
||||
for (bitAt = 0; bitAt < bitCount; bitAt++) {
|
||||
if (encodedBits.at(bitAt)) {
|
||||
encodedByteCount++;
|
||||
leadBits++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -228,10 +230,12 @@ template<typename Enum> inline void PropertyFlags<Enum>::decode(const QByteArray
|
|||
int expectedBitCount = encodedByteCount * BITS_PER_BYTE;
|
||||
|
||||
// Now, keep reading...
|
||||
int flagsStartAt = bitAt + 1;
|
||||
for (bitAt = flagsStartAt; bitAt < expectedBitCount; bitAt++) {
|
||||
if (encodedBits.at(bitAt)) {
|
||||
setHasProperty((Enum)(bitAt - flagsStartAt));
|
||||
if (expectedBitCount <= (encodedBits.size() - leadBits)) {
|
||||
int flagsStartAt = bitAt + 1;
|
||||
for (bitAt = flagsStartAt; bitAt < expectedBitCount; bitAt++) {
|
||||
if (encodedBits.at(bitAt)) {
|
||||
setHasProperty((Enum)(bitAt - flagsStartAt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <QColor>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
|
||||
#include "RegisteredMetaTypes.h"
|
||||
|
@ -29,6 +30,7 @@ void registerMetaTypes(QScriptEngine* engine) {
|
|||
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
|
||||
|
@ -129,6 +131,14 @@ void qColorFromScriptValue(const QScriptValue& object, QColor& color) {
|
|||
}
|
||||
}
|
||||
|
||||
QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url) {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
void qURLFromScriptValue(const QScriptValue& object, QUrl& url) {
|
||||
url = object.toString();
|
||||
}
|
||||
|
||||
QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
QScriptValue origin = vec3toScriptValue(engine, pickRay.origin);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue