Merge branch 'master' of https://github.com/highfidelity/hifi into domain-tunnel

This commit is contained in:
Stephen Birarda 2014-10-17 14:48:43 -07:00
commit e72b86059e
75 changed files with 2097 additions and 3320 deletions

View file

@ -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")

1891
Doxyfile

File diff suppressed because it is too large Load diff

View file

@ -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;
}
}
}
}
}
}

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -42,7 +42,7 @@ public:
int parseData(const QByteArray& packet);
void checkBuffersBeforeFrameSend(AABox* checkSourceZone, AABox* listenerZone);
void checkBuffersBeforeFrameSend();
void removeDeadInjectedStreams();

View file

@ -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",
@ -64,7 +64,7 @@
"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.",
"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 the domain-server machine.",
"numbered": false,
"columns": [
{
@ -77,10 +77,24 @@
]
},
{
"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",
@ -90,7 +104,7 @@
"key": {
"name": "name",
"label": "Name",
"placeholder": "Zone name"
"placeholder": "Zone_Name"
},
"columns": [
{
@ -118,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",
@ -139,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",

View file

@ -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;

View file

@ -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'></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
@ -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

View file

@ -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>

View file

@ -11,6 +11,7 @@
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <QtCore/QDir>
#include <QtCore/QJsonDocument>
@ -615,60 +616,62 @@ 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(),
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
ALLOWED_USERS_SETTINGS_KEYPATH);
static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
// 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) {
return true;
}
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.";
}
}
if (allowedUsers.contains(username)) {
// it's possible this user can be allowed to connect, but we need to check their username signature
requestUserPublicKey(username);
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_RSA_PUBKEY(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 {
qDebug() << "Connect request denied for user" << username << "not in allowed users list.";
}
} else {
// since we have no allowed user list, let them all in
@ -1205,17 +1208,20 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
QDataStream packetStream(receivedPacket);
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
int numNodeInfoBytes = parseNodeDataFromByteArray(packetStream, throwawayNodeType,
nodePublicAddress, nodeLocalAddress,
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;

View 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;
}

View file

@ -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();
@ -174,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);
@ -2423,6 +2453,32 @@ SelectionDisplay = (function () {
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
View 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);

View file

@ -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;
}
@ -292,6 +322,7 @@ var toolBar = (function () {
}
return false;
};
@ -339,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 {
@ -442,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;
}
@ -482,6 +514,7 @@ function mouseReleaseEvent(event) {
if (entitySelected) {
tooltip.show(false);
}
entityCameraTool.mouseReleaseEvent(event);
}
Controller.mousePressEvent.connect(mousePressEvent);
@ -613,6 +646,10 @@ Controller.keyReleaseEvent.connect(function (event) {
}
if (event.text == "BACKSPACE") {
handeMenuEvent("Delete");
} else if (event.text == "f") {
if (entitySelected) {
entityCameraTool.focus(selectedEntityProperties);
}
}
});

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -177,7 +177,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);
@ -587,7 +588,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);
@ -685,6 +687,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();
@ -1540,9 +1546,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 {
@ -1830,7 +1835,6 @@ void Application::init() {
// initialize our face trackers after loading the menu settings
_faceshift.init();
_faceplus.init();
_visage.init();
Leapmotion::init();
@ -3076,6 +3080,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
@ -3107,14 +3115,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);
@ -3182,7 +3193,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);
}
@ -3367,7 +3378,6 @@ void Application::resetSensors() {
_mouseX = _glWidget->width() / 2;
_mouseY = _glWidget->height() / 2;
_faceplus.reset();
_faceshift.reset();
_visage.reset();
_dde.reset();
@ -4217,3 +4227,7 @@ void Application::takeSnapshot() {
}
_snapshotShareDialog->show();
}
void Application::setRenderResolutionScale(float scale) {
_renderResolutionScale = scale;
}

View file

@ -58,7 +58,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"
@ -214,7 +213,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; }
@ -301,6 +299,8 @@ public:
bool isLookingAtMyAvatar(Avatar* avatar);
float getRenderResolutionScale() const { return _renderResolutionScale; }
signals:
/// Fired when we're simulating; allows external parties to hook in.
@ -363,6 +363,8 @@ public slots:
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
void setRenderResolutionScale(float scale);
void resetSensors();
private slots:
@ -402,7 +404,6 @@ private:
// Various helper functions called during update()
void updateLOD();
void updateMouseRay();
void updateFaceplus();
void updateFaceshift();
void updateVisage();
void updateDDE();
@ -503,7 +504,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;
@ -618,6 +618,8 @@ private:
quint64 _lastNackTime;
quint64 _lastSendDownstreamAudioStats;
float _renderResolutionScale;
};
#endif // hifi_Application_h

View file

@ -26,7 +26,6 @@
#include "AudioSourceTone.h"
#include "AudioSourceNoise.h"
#include "AudioGain.h"
#include "AudioPan.h"
#include "AudioFilter.h"
#include "AudioFilterBank.h"

View file

@ -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);

View file

@ -140,6 +140,7 @@ void DatagramProcessor::processDatagrams() {
// 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;
}

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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()));
@ -377,7 +378,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,
@ -386,11 +395,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()));
@ -403,10 +408,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()));
@ -422,6 +423,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);
@ -1231,6 +1236,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()) {
@ -1529,6 +1551,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;
}
@ -1551,6 +1574,7 @@ void Menu::autoAdjustLOD(float currentFPS) {
}
if (changed) {
_shouldRenderTableNeedsRebuilding = true;
if (_lodToolsDialog) {
_lodToolsDialog->reloadSliders();
}
@ -1565,14 +1589,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());

View file

@ -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;
};
@ -366,6 +372,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";
@ -390,7 +399,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";
@ -398,7 +406,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";
@ -441,6 +448,12 @@ 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";

View file

@ -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);
}

View file

@ -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:

View file

@ -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?

View file

@ -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();

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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
}

View file

@ -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

View file

@ -15,9 +15,9 @@
#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) :
_sdlGameController(sdlGameController),

View file

@ -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()));
}

View file

@ -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++;

View file

@ -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

View file

@ -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]);

View file

@ -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);
}

View file

@ -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;

View file

@ -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) {

View file

@ -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>)

View file

@ -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(),

View file

@ -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

View file

@ -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);
}

View file

@ -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();

View file

@ -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...

View file

@ -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

View file

@ -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>

View file

@ -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)
{
}

View file

@ -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

View file

@ -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++;

View file

@ -115,6 +115,8 @@ public:
FBXTexture diffuseTexture;
FBXTexture normalTexture;
FBXTexture specularTexture;
QString materialID;
};
/// A single mesh (with optional blendshapes) extracted from an FBX document.

View file

@ -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));
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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());

View file

@ -150,9 +150,6 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() {
qDebug() << "Could not create RSA struct from QByteArray private key.";
qDebug() << "Will re-attempt on next domain-server check in.";
}
} else {
qDebug() << "No private key present in DataServerAccountInfo. Re-log to generate new key.";
qDebug() << "Returning empty username signature.";
}
}

View file

@ -315,8 +315,6 @@ void NodeList::sendDomainServerCheckIn() {
if (!usernameSignature.isEmpty()) {
qDebug() << "Including username signature in domain connect request.";
packetStream << usernameSignature;
} else {
qDebug() << "Private key not present - domain connect request cannot include username signature";
}
}

View file

@ -85,7 +85,7 @@ PacketVersion versionForPacketType(PacketType type) {
case PacketTypeAudioStreamStats:
return 1;
case PacketTypeMetavoxelData:
return 6;
return 7;
case PacketTypeVoxelData:
return VERSION_VOXELS_HAS_FILE_BREAKS;
default:

View file

@ -66,11 +66,11 @@ void RSAKeypairGenerator::generateKeypair() {
// cleanup the public and private key DER data, if required
if (publicKeyLength > 0) {
delete publicKeyDER;
OPENSSL_free(publicKeyDER);
}
if (privateKeyLength > 0) {
delete privateKeyDER;
OPENSSL_free(privateKeyDER);
}
return;
@ -84,8 +84,8 @@ void RSAKeypairGenerator::generateKeypair() {
QByteArray privateKeyArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength);
// cleanup the publicKeyDER and publicKeyDER data
delete publicKeyDER;
delete privateKeyDER;
OPENSSL_free(publicKeyDER);
OPENSSL_free(privateKeyDER);
emit generatedKeypair(publicKeyArray, privateKeyArray);
}

View file

@ -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() {

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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));
}
}
}
}

View file

@ -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);

View file

@ -20,6 +20,7 @@
#include "SharedUtil.h"
class QColor;
class QUrl;
Q_DECLARE_METATYPE(glm::vec4)
Q_DECLARE_METATYPE(glm::vec3)
@ -47,6 +48,9 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color);
QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color);
void qColorFromScriptValue(const QScriptValue& object, QColor& color);
QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url);
void qURLFromScriptValue(const QScriptValue& object, QUrl& url);
class PickRay {
public:
PickRay() : origin(0), direction(0) { };

View file

@ -15,6 +15,7 @@
#include <QtScript/QScriptEngine>
#include <AACube.h>
#include <OctreeConstants.h>
#include <SharedUtil.h>
#include "VoxelConstants.h"
@ -53,8 +54,6 @@ void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, Ray
inline QDebug operator<<(QDebug debug, const VoxelDetail& details) {
const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe
debug << "VoxelDetail[ ("
<< details.x * (float)TREE_SCALE << "," << details.y * (float)TREE_SCALE << "," << details.z * (float)TREE_SCALE
<< " ) to ("