From e620518d2f938c14ae36717d8ec8ced13a436852 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 17:59:30 -0700 Subject: [PATCH 01/99] Working on streaming script values. --- libraries/metavoxels/src/Bitstream.cpp | 92 ++++++++++++++++++++++++++ libraries/metavoxels/src/Bitstream.h | 4 ++ 2 files changed, 96 insertions(+) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d9242ad9b7..63ef8b09bb 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -543,6 +543,98 @@ Bitstream& Bitstream::operator>>(QScriptString& string) { return *this; } +enum ScriptValueType { + INVALID_SCRIPT_VALUE, + UNDEFINED_SCRIPT_VALUE, + NULL_SCRIPT_VALUE, + BOOL_SCRIPT_VALUE, + NUMBER_SCRIPT_VALUE, + STRING_SCRIPT_VALUE, + QVARIANT_SCRIPT_VALUE, + QOBJECT_SCRIPT_VALUE, + DATE_SCRIPT_VALUE, + REGEXP_SCRIPT_VALUE, + ARRAY_SCRIPT_VALUE, + OBJECT_SCRIPT_VALUE +}; + +const int SCRIPT_VALUE_BITS = 4; + +void writeScriptValueType(Bitstream& out, ScriptValueType type) { + out.write(&type, SCRIPT_VALUE_BITS); +} + +ScriptValueType readScriptValueType(Bitstream& in) { + ScriptValueType type = (ScriptValueType)0; + in.read(&type, SCRIPT_VALUE_BITS); + return type; +} + +Bitstream& Bitstream::operator<<(const QScriptValue& value) { + if (value.isUndefined()) { + writeScriptValueType(*this, UNDEFINED_SCRIPT_VALUE); + + } else if (value.isNull()) { + writeScriptValueType(*this, NULL_SCRIPT_VALUE); + + } else if (value.isBool()) { + writeScriptValueType(*this, BOOL_SCRIPT_VALUE); + + } else if (value.isNumber()) { + writeScriptValueType(*this, NUMBER_SCRIPT_VALUE); + + } else if (value.isString()) { + writeScriptValueType(*this, STRING_SCRIPT_VALUE); + + } else if (value.isArray()) { + writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); + + } else if (value.isObject()) { + writeScriptValueType(*this, OBJECT_SCRIPT_VALUE); + + } else { + writeScriptValueType(*this, INVALID_SCRIPT_VALUE); + } + return *this; +} + +Bitstream& Bitstream::operator>>(QScriptValue& value) { + switch (readScriptValueType(*this)) { + case UNDEFINED_SCRIPT_VALUE: + value = QScriptValue(QScriptValue::UndefinedValue); + break; + + case NULL_SCRIPT_VALUE: + value = QScriptValue(QScriptValue::NullValue); + break; + + case BOOL_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case NUMBER_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case STRING_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case ARRAY_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case OBJECT_SCRIPT_VALUE: + value = QScriptValue(); + break; + + default: + value = QScriptValue(); + break; + } + return *this; +} + Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) { _sharedObjectStreamer << object; return *this; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 80adfc4e8b..3b1459109c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -29,6 +29,7 @@ class QByteArray; class QColor; class QDataStream; +class QScriptValue; class QUrl; class Attribute; @@ -372,6 +373,9 @@ public: Bitstream& operator<<(const QScriptString& string); Bitstream& operator>>(QScriptString& string); + Bitstream& operator<<(const QScriptValue& value); + Bitstream& operator>>(QScriptValue& value); + Bitstream& operator<<(const SharedObjectPointer& object); Bitstream& operator>>(SharedObjectPointer& object); From 4d423c679c2700646d7d54bd6a4f9dd3a65d84a7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 10:21:55 -0700 Subject: [PATCH 02/99] Added some typedefs to simplify the various mapping types. --- libraries/metavoxels/src/Bitstream.cpp | 42 +++++++++++++------------- libraries/metavoxels/src/Bitstream.h | 20 ++++++++---- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 63ef8b09bb..e621484986 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -653,7 +653,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { if (_metadataType == NO_METADATA) { return *this; } - const QVector& propertyWriters = getPropertyWriters().value(metaObject); + const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); *this << propertyWriters.size(); QCryptographicHash hash(QCryptographicHash::Md5); foreach (const PropertyWriter& propertyWriter, propertyWriters) { @@ -692,7 +692,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { } int storedPropertyCount; *this >> storedPropertyCount; - QVector properties(storedPropertyCount); + PropertyReaderVector properties(storedPropertyCount); for (int i = 0; i < storedPropertyCount; i++) { TypeReader typeReader; *this >> typeReader; @@ -711,7 +711,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; if (metaObject) { - const QVector& propertyWriters = getPropertyWriters().value(metaObject); + const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); if (propertyWriters.size() == properties.size()) { for (int i = 0; i < propertyWriters.size(); i++) { const PropertyWriter& propertyWriter = propertyWriters.at(i); @@ -1082,17 +1082,17 @@ QHash& Bitstream::getTypeStreamers() { return typeStreamers; } -const QHash, const TypeStreamer*>& Bitstream::getEnumStreamers() { - static QHash, const TypeStreamer*> enumStreamers = createEnumStreamers(); +const QHash& Bitstream::getEnumStreamers() { + static QHash enumStreamers = createEnumStreamers(); return enumStreamers; } -QHash, const TypeStreamer*> Bitstream::createEnumStreamers() { - QHash, const TypeStreamer*> enumStreamers; +QHash Bitstream::createEnumStreamers() { + QHash enumStreamers; foreach (const QMetaObject* metaObject, getMetaObjects()) { for (int i = 0; i < metaObject->enumeratorCount(); i++) { QMetaEnum metaEnum = metaObject->enumerator(i); - const TypeStreamer*& streamer = enumStreamers[QPair(metaEnum.scope(), metaEnum.name())]; + const TypeStreamer*& streamer = enumStreamers[ScopeNamePair(metaEnum.scope(), metaEnum.name())]; if (!streamer) { streamer = new EnumTypeStreamer(metaEnum); } @@ -1114,15 +1114,15 @@ QHash Bitstream::createEnumStreamersByName() { return enumStreamersByName; } -const QHash >& Bitstream::getPropertyReaders() { - static QHash > propertyReaders = createPropertyReaders(); +const QHash& Bitstream::getPropertyReaders() { + static QHash propertyReaders = createPropertyReaders(); return propertyReaders; } -QHash > Bitstream::createPropertyReaders() { - QHash > propertyReaders; +QHash Bitstream::createPropertyReaders() { + QHash propertyReaders; foreach (const QMetaObject* metaObject, getMetaObjects()) { - QVector& readers = propertyReaders[metaObject]; + PropertyReaderVector& readers = propertyReaders[metaObject]; for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (!property.isStored()) { @@ -1131,7 +1131,7 @@ QHash > Bitstream::createPropertyRea const TypeStreamer* streamer; if (property.isEnumType()) { QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(QPair( + streamer = getEnumStreamers().value(ScopeNamePair( QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); } else { @@ -1145,15 +1145,15 @@ QHash > Bitstream::createPropertyRea return propertyReaders; } -const QHash >& Bitstream::getPropertyWriters() { - static QHash > propertyWriters = createPropertyWriters(); +const QHash& Bitstream::getPropertyWriters() { + static QHash propertyWriters = createPropertyWriters(); return propertyWriters; } -QHash > Bitstream::createPropertyWriters() { - QHash > propertyWriters; +QHash Bitstream::createPropertyWriters() { + QHash propertyWriters; foreach (const QMetaObject* metaObject, getMetaObjects()) { - QVector& writers = propertyWriters[metaObject]; + PropertyWriterVector& writers = propertyWriters[metaObject]; for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (!property.isStored()) { @@ -1162,7 +1162,7 @@ QHash > Bitstream::createPropertyWri const TypeStreamer* streamer; if (property.isEnumType()) { QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(QPair( + streamer = getEnumStreamers().value(ScopeNamePair( QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); } else { @@ -1416,7 +1416,7 @@ void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVarian } ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, - const QVector& properties) : + const PropertyReaderVector& properties) : _className(className), _metaObject(metaObject), _properties(properties) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 3b1459109c..4fe136f720 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -45,6 +45,10 @@ class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; +typedef QPair ScopeNamePair; +typedef QVector PropertyReaderVector; +typedef QVector PropertyWriterVector; + /// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that /// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows /// us to use the minimum number of bits to encode the IDs. @@ -426,14 +430,18 @@ private: static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); - static const QHash, const TypeStreamer*>& getEnumStreamers(); - static QHash, const TypeStreamer*> createEnumStreamers(); + + static const QHash& getEnumStreamers(); + static QHash createEnumStreamers(); + static const QHash& getEnumStreamersByName(); static QHash createEnumStreamersByName(); - static const QHash >& getPropertyReaders(); - static QHash > createPropertyReaders(); - static const QHash >& getPropertyWriters(); - static QHash > createPropertyWriters(); + + static const QHash& getPropertyReaders(); + static QHash createPropertyReaders(); + + static const QHash& getPropertyWriters(); + static QHash createPropertyWriters(); }; template inline void Bitstream::writeDelta(const T& value, const T& reference) { From ae22aa1a215d5fa540082d346b9aff24ece51946 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 10:53:44 -0700 Subject: [PATCH 03/99] Working on JS type streaming. --- libraries/metavoxels/src/Bitstream.cpp | 72 +++++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 12 +++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e621484986..8f813adf90 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -350,6 +350,14 @@ Bitstream& Bitstream::operator>>(uint& value) { return *this; } +Bitstream& Bitstream::operator<<(qint64 value) { + return write(&value, 64); +} + +Bitstream& Bitstream::operator>>(qint64& value) { + return read(&value, 64); +} + Bitstream& Bitstream::operator<<(float value) { return write(&value, 32); } @@ -358,6 +366,14 @@ Bitstream& Bitstream::operator>>(float& value) { return read(&value, 32); } +Bitstream& Bitstream::operator<<(double value) { + return write(&value, 64); +} + +Bitstream& Bitstream::operator>>(double& value) { + return read(&value, 64); +} + Bitstream& Bitstream::operator<<(const glm::vec3& value) { return *this << value.x << value.y << value.z; } @@ -420,6 +436,36 @@ Bitstream& Bitstream::operator>>(QUrl& url) { return *this; } +Bitstream& Bitstream::operator<<(const QDateTime& dateTime) { + return *this << dateTime.toMSecsSinceEpoch(); +} + +Bitstream& Bitstream::operator>>(QDateTime& dateTime) { + qint64 msecsSinceEpoch; + *this >> msecsSinceEpoch; + dateTime = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch); + return *this; +} + +Bitstream& Bitstream::operator<<(const QRegExp& regExp) { + *this << regExp.pattern(); + + return *this; +} + +Bitstream& Bitstream::operator>>(QRegExp& regExp) { + QString pattern; + *this >> pattern; + Qt::CaseSensitivity caseSensitivity = (Qt::CaseSensitivity)0; + read(&caseSensitivity, 1); + QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0; + read(&syntax, 3); + + regExp = QRegExp(pattern, caseSensitivity, syntax); + + return *this; +} + Bitstream& Bitstream::operator<<(const QVariant& value) { if (!value.isValid()) { _typeStreamerStreamer << NULL; @@ -550,8 +596,9 @@ enum ScriptValueType { BOOL_SCRIPT_VALUE, NUMBER_SCRIPT_VALUE, STRING_SCRIPT_VALUE, - QVARIANT_SCRIPT_VALUE, + VARIANT_SCRIPT_VALUE, QOBJECT_SCRIPT_VALUE, + QMETAOBJECT_SCRIPT_VALUE, DATE_SCRIPT_VALUE, REGEXP_SCRIPT_VALUE, ARRAY_SCRIPT_VALUE, @@ -579,12 +626,35 @@ Bitstream& Bitstream::operator<<(const QScriptValue& value) { } else if (value.isBool()) { writeScriptValueType(*this, BOOL_SCRIPT_VALUE); + *this << value.toBool(); } else if (value.isNumber()) { writeScriptValueType(*this, NUMBER_SCRIPT_VALUE); + *this << value.toNumber(); } else if (value.isString()) { writeScriptValueType(*this, STRING_SCRIPT_VALUE); + *this << value.toString(); + + } else if (value.isVariant()) { + writeScriptValueType(*this, VARIANT_SCRIPT_VALUE); + *this << value.toVariant(); + + } else if (value.isQObject()) { + writeScriptValueType(*this, QOBJECT_SCRIPT_VALUE); + *this << value.toQObject(); + + } else if (value.isQMetaObject()) { + writeScriptValueType(*this, QMETAOBJECT_SCRIPT_VALUE); + *this << value.toQMetaObject(); + + } else if (value.isDate()) { + writeScriptValueType(*this, DATE_SCRIPT_VALUE); + *this << value.toDateTime(); + + } else if (value.isRegExp()) { + writeScriptValueType(*this, REGEXP_SCRIPT_VALUE); + *this << value.toRegExp(); } else if (value.isArray()) { writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 4fe136f720..83733674a4 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -321,9 +321,15 @@ public: Bitstream& operator<<(uint value); Bitstream& operator>>(uint& value); + Bitstream& operator<<(qint64 value); + Bitstream& operator>>(qint64& value); + Bitstream& operator<<(float value); Bitstream& operator>>(float& value); + Bitstream& operator<<(double value); + Bitstream& operator>>(double& value); + Bitstream& operator<<(const glm::vec3& value); Bitstream& operator>>(glm::vec3& value); @@ -342,6 +348,12 @@ public: Bitstream& operator<<(const QUrl& url); Bitstream& operator>>(QUrl& url); + Bitstream& operator<<(const QDateTime& dateTime); + Bitstream& operator>>(QDateTime& dateTime); + + Bitstream& operator<<(const QRegExp& regExp); + Bitstream& operator>>(QRegExp& regExp); + Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); From 58bd1f7ab57c312933370a28242b1f8c8b68d97f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 11:54:43 -0700 Subject: [PATCH 04/99] More script streaming bits. --- libraries/metavoxels/src/Bitstream.cpp | 110 ++++++++++++++++++++----- 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 8f813adf90..c3d0348ee8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -449,8 +450,11 @@ Bitstream& Bitstream::operator>>(QDateTime& dateTime) { Bitstream& Bitstream::operator<<(const QRegExp& regExp) { *this << regExp.pattern(); - - return *this; + Qt::CaseSensitivity caseSensitivity = regExp.caseSensitivity(); + write(&caseSensitivity, 1); + QRegExp::PatternSyntax syntax = regExp.patternSyntax(); + write(&syntax, 3); + return *this << regExp.isMinimal(); } Bitstream& Bitstream::operator>>(QRegExp& regExp) { @@ -460,9 +464,10 @@ Bitstream& Bitstream::operator>>(QRegExp& regExp) { read(&caseSensitivity, 1); QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0; read(&syntax, 3); - regExp = QRegExp(pattern, caseSensitivity, syntax); - + bool minimal; + *this >> minimal; + regExp.setMinimal(minimal); return *this; } @@ -658,10 +663,20 @@ Bitstream& Bitstream::operator<<(const QScriptValue& value) { } else if (value.isArray()) { writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); - + int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + *this << length; + for (int i = 0; i < length; i++) { + *this << value.property(i); + } } else if (value.isObject()) { writeScriptValueType(*this, OBJECT_SCRIPT_VALUE); - + for (QScriptValueIterator it(value); it.hasNext(); ) { + it.next(); + *this << it.scriptName(); + *this << it.value(); + } + *this << QScriptString(); + } else { writeScriptValueType(*this, INVALID_SCRIPT_VALUE); } @@ -678,26 +693,79 @@ Bitstream& Bitstream::operator>>(QScriptValue& value) { value = QScriptValue(QScriptValue::NullValue); break; - case BOOL_SCRIPT_VALUE: - value = QScriptValue(); + case BOOL_SCRIPT_VALUE: { + bool boolValue; + *this >> boolValue; + value = QScriptValue(boolValue); break; - - case NUMBER_SCRIPT_VALUE: - value = QScriptValue(); + } + case NUMBER_SCRIPT_VALUE: { + qsreal numberValue; + *this >> numberValue; + value = QScriptValue(numberValue); break; - - case STRING_SCRIPT_VALUE: - value = QScriptValue(); + } + case STRING_SCRIPT_VALUE: { + QString stringValue; + *this >> stringValue; + value = QScriptValue(stringValue); break; - - case ARRAY_SCRIPT_VALUE: - value = QScriptValue(); + } + case VARIANT_SCRIPT_VALUE: { + QVariant variantValue; + *this >> variantValue; + value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue); break; - - case OBJECT_SCRIPT_VALUE: - value = QScriptValue(); + } + case QOBJECT_SCRIPT_VALUE: { + QObject* object; + *this >> object; + ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); break; - + } + case QMETAOBJECT_SCRIPT_VALUE: { + const QMetaObject* metaObject; + *this >> metaObject; + ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + break; + } + case DATE_SCRIPT_VALUE: { + QDateTime dateTime; + *this >> dateTime; + value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + break; + } + case REGEXP_SCRIPT_VALUE: { + QRegExp regExp; + *this >> regExp; + value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + break; + } + case ARRAY_SCRIPT_VALUE: { + int length; + *this >> length; + value = ScriptCache::getInstance()->getEngine()->newArray(length); + for (int i = 0; i < length; i++) { + QScriptValue element; + *this >> element; + value.setProperty(i, element); + } + break; + } + case OBJECT_SCRIPT_VALUE: { + value = ScriptCache::getInstance()->getEngine()->newObject(); + forever { + QScriptString name; + *this >> name; + if (!name.isValid()) { + break; + } + QScriptValue scriptValue; + *this >> scriptValue; + value.setProperty(name, scriptValue); + } + break; + } default: value = QScriptValue(); break; From fd469c543c7b8cb7ef778ad280adda64bb3f6768 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 14:35:20 -0700 Subject: [PATCH 05/99] More script streaming bits. --- libraries/metavoxels/src/Bitstream.cpp | 276 ++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 4 + libraries/metavoxels/src/ScriptCache.cpp | 79 +++++++ libraries/metavoxels/src/ScriptCache.h | 5 + 4 files changed, 362 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index c3d0348ee8..d27053aefb 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -31,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(uint) REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QColor) +REGISTER_SIMPLE_TYPE_STREAMER(QScriptValue) REGISTER_SIMPLE_TYPE_STREAMER(QString) REGISTER_SIMPLE_TYPE_STREAMER(QUrl) REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) @@ -286,6 +287,12 @@ void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) { streamer->writeRawDelta(*this, value, reference); } +void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) { + const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); + _typeStreamerStreamer << streamer; + streamer->writeRawDelta(*this, value, reference); +} + void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { TypeReader typeReader; _typeStreamerStreamer >> typeReader; @@ -310,6 +317,271 @@ void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { value = objectReader.readDelta(*this, reference); } +void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) { + if (reference.isUndefined() || reference.isNull()) { + *this << value; + + } else if (reference.isBool()) { + if (value.isBool()) { + *this << false; + *this << value.toBool(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isNumber()) { + if (value.isNumber()) { + *this << false; + *this << value.toNumber(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isString()) { + if (value.isString()) { + *this << false; + *this << value.toString(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isVariant()) { + if (value.isVariant()) { + *this << false; + writeRawDelta(value.toVariant(), reference.toVariant()); + + } else { + *this << true; + *this << value; + } + } else if (reference.isQObject()) { + if (value.isQObject()) { + *this << false; + writeRawDelta(value.toQObject(), reference.toQObject()); + + } else { + *this << true; + *this << value; + } + } else if (reference.isQMetaObject()) { + if (value.isQMetaObject()) { + *this << false; + *this << value.toQMetaObject(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isDate()) { + if (value.isDate()) { + *this << false; + *this << value.toDateTime(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isRegExp()) { + if (value.isRegExp()) { + *this << false; + *this << value.toRegExp(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isArray()) { + if (value.isArray()) { + *this << false; + int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + *this << length; + int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + for (int i = 0; i < length; i++) { + if (i < referenceLength) { + writeDelta(value.property(i), reference.property(i)); + } else { + *this << value.property(i); + } + } + } else { + *this << true; + *this << value; + } + } else if (reference.isObject()) { + if (value.isObject()) { + *this << false; + for (QScriptValueIterator it(value); it.hasNext(); ) { + it.next(); + QScriptValue referenceValue = reference.property(it.scriptName()); + if (it.value() != referenceValue) { + *this << it.scriptName(); + writeRawDelta(it.value(), referenceValue); + } + } + for (QScriptValueIterator it(reference); it.hasNext(); ) { + it.next(); + if (!value.property(it.scriptName()).isValid()) { + *this << it.scriptName(); + writeRawDelta(QScriptValue(), it.value()); + } + } + *this << QScriptString(); + + } else { + *this << true; + *this << value; + } + } else { + *this << value; + } +} + +void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) { + if (reference.isUndefined() || reference.isNull()) { + *this >> value; + + } else if (reference.isBool()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + bool boolValue; + *this >> boolValue; + value = QScriptValue(boolValue); + } + } else if (reference.isNumber()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + qsreal numberValue; + *this >> numberValue; + value = QScriptValue(numberValue); + } + } else if (reference.isString()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QString stringValue; + *this >> stringValue; + value = QScriptValue(stringValue); + } + } else if (reference.isVariant()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QVariant variant; + readRawDelta(variant, reference.toVariant()); + value = ScriptCache::getInstance()->getEngine()->newVariant(variant); + } + } else if (reference.isQObject()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QObject* object; + readRawDelta(object, reference.toQObject()); + value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); + } + } else if (reference.isQMetaObject()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + const QMetaObject* metaObject; + *this >> metaObject; + value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + } + } else if (reference.isDate()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QDateTime dateTime; + *this >> dateTime; + value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + } + } else if (reference.isRegExp()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QRegExp regExp; + *this >> regExp; + value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + } + } else if (reference.isArray()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + int length; + *this >> length; + value = ScriptCache::getInstance()->getEngine()->newArray(length); + int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + for (int i = 0; i < length; i++) { + QScriptValue element; + if (i < referenceLength) { + readDelta(element, reference.property(i)); + } else { + *this >> element; + } + value.setProperty(i, element); + } + } + } else if (reference.isObject()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + // start by shallow-copying the reference + value = ScriptCache::getInstance()->getEngine()->newObject(); + for (QScriptValueIterator it(reference); it.hasNext(); ) { + it.next(); + value.setProperty(it.scriptName(), it.value()); + } + // then apply the requested changes + forever { + QScriptString name; + *this >> name; + if (!name.isValid()) { + break; + } + QScriptValue scriptValue; + readRawDelta(scriptValue, reference.property(name)); + value.setProperty(name, scriptValue); + } + } + } else { + *this >> value; + } +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -612,11 +884,11 @@ enum ScriptValueType { const int SCRIPT_VALUE_BITS = 4; -void writeScriptValueType(Bitstream& out, ScriptValueType type) { +static void writeScriptValueType(Bitstream& out, ScriptValueType type) { out.write(&type, SCRIPT_VALUE_BITS); } -ScriptValueType readScriptValueType(Bitstream& in) { +static ScriptValueType readScriptValueType(Bitstream& in) { ScriptValueType type = (ScriptValueType)0; in.read(&type, SCRIPT_VALUE_BITS); return type; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 83733674a4..d05f6574c0 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -292,11 +292,15 @@ public: template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); + void writeRawDelta(const QVariant& value, const QVariant& reference); void readRawDelta(QVariant& value, const QVariant& reference); void writeRawDelta(const QObject* value, const QObject* reference); void readRawDelta(QObject*& value, const QObject* reference); + void writeRawDelta(const QScriptValue& value, const QScriptValue& reference); + void readRawDelta(QScriptValue& value, const QScriptValue& reference); + template void writeRawDelta(const T& value, const T& reference); template void readRawDelta(T& value, const T& reference); diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index dd090613b7..9de624fb29 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -13,11 +13,90 @@ #include #include +#include #include #include "AttributeRegistry.h" #include "ScriptCache.h" +static int scriptValueMetaTypeId = qRegisterMetaType(); + +bool operator==(const QScriptValue& first, const QScriptValue& second) { + if (first.isUndefined()) { + return second.isUndefined(); + + } else if (first.isNull()) { + return second.isNull(); + + } else if (first.isBool()) { + return second.isBool() && first.toBool() == second.toBool(); + + } else if (first.isNumber()) { + return second.isNumber() && first.toNumber() == second.toNumber(); + + } else if (first.isString()) { + return second.isString() && first.toString() == second.toString(); + + } else if (first.isVariant()) { + return second.isVariant() && first.toVariant() == second.toVariant(); + + } else if (first.isQObject()) { + return second.isQObject() && first.toQObject() == second.toQObject(); + + } else if (first.isQMetaObject()) { + return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject(); + + } else if (first.isDate()) { + return second.isDate() && first.toDateTime() == second.toDateTime(); + + } else if (first.isRegExp()) { + return second.isRegExp() && first.toRegExp() == second.toRegExp(); + + } else if (first.isArray()) { + if (!second.isArray()) { + return false; + } + int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) { + return false; + } + for (int i = 0; i < length; i++) { + if (first.property(i) != second.property(i)) { + return false; + } + } + return true; + + } else if (first.isObject()) { + if (!second.isObject()) { + return false; + } + int propertyCount = 0; + for (QScriptValueIterator it(first); it.hasNext(); ) { + it.next(); + if (second.property(it.scriptName()) != it.value()) { + return false; + } + propertyCount++; + } + // make sure the second has exactly as many properties as the first + for (QScriptValueIterator it(second); it.hasNext(); ) { + it.next(); + if (--propertyCount < 0) { + return false; + } + } + return true; + + } else { + return !second.isValid(); + } +} + +bool operator!=(const QScriptValue& first, const QScriptValue& second) { + return !(first == second); +} + ScriptCache* ScriptCache::getInstance() { static ScriptCache cache; return &cache; diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index f393d0e0a8..d88a967081 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -65,6 +65,11 @@ private: QScriptString _generatorString; }; +Q_DECLARE_METATYPE(QScriptValue) + +bool operator==(const QScriptValue& first, const QScriptValue& second); +bool operator!=(const QScriptValue& first, const QScriptValue& second); + /// A program loaded from the network. class NetworkProgram : public Resource { Q_OBJECT From c8c8bccbf3b7768c88791ce2e6f37fe10b2a319a Mon Sep 17 00:00:00 2001 From: matsukaze Date: Mon, 9 Jun 2014 17:52:50 -0400 Subject: [PATCH 06/99] Job #19766 BUG: Stop or reload all scripts crashes interface fixed. QUrl(name).toString() does not equal name, therefore removing name from ScriptEngineHash was failing, and a dangling pointer was left in the script engine hash map. --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 574df09ee2..9fb821bff4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3494,7 +3494,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript // start the script on a new thread... QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptName, scriptEngine); + _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; From 590f045ed1ce8806f964d252042a173c6f15ff12 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 16:51:41 -0700 Subject: [PATCH 07/99] Script value streaming tests, fixes. --- libraries/metavoxels/src/Bitstream.cpp | 9 ++- libraries/metavoxels/src/ScriptCache.cpp | 5 ++ libraries/metavoxels/src/ScriptCache.h | 1 + tests/metavoxels/src/MetavoxelTests.cpp | 88 +++++++++++++++++++++++- tests/metavoxels/src/MetavoxelTests.h | 9 ++- 5 files changed, 106 insertions(+), 6 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d27053aefb..7595580c33 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -411,7 +411,7 @@ void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& ref *this << value; } } else if (reference.isObject()) { - if (value.isObject()) { + if (value.isObject()) { *this << false; for (QScriptValueIterator it(value); it.hasNext(); ) { it.next(); @@ -1401,14 +1401,17 @@ Bitstream& Bitstream::operator>(AttributePointer& attribute) { return *this; } +const QString INVALID_STRING("%INVALID%"); + Bitstream& Bitstream::operator<(const QScriptString& string) { - return *this << string.toString(); + return *this << (string.isValid() ? string.toString() : INVALID_STRING); } Bitstream& Bitstream::operator>(QScriptString& string) { QString rawString; *this >> rawString; - string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); + string = (rawString == INVALID_STRING) ? QScriptString() : + ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); return *this; } diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 9de624fb29..9648a047cb 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -20,6 +20,7 @@ #include "ScriptCache.h" static int scriptValueMetaTypeId = qRegisterMetaType(); +static bool scriptValueComparators = QMetaType::registerComparators(); bool operator==(const QScriptValue& first, const QScriptValue& second) { if (first.isUndefined()) { @@ -97,6 +98,10 @@ bool operator!=(const QScriptValue& first, const QScriptValue& second) { return !(first == second); } +bool operator<(const QScriptValue& first, const QScriptValue& second) { + return first.lessThan(second); +} + ScriptCache* ScriptCache::getInstance() { static ScriptCache cache; return &cache; diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index d88a967081..5d29157b3d 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -69,6 +69,7 @@ Q_DECLARE_METATYPE(QScriptValue) bool operator==(const QScriptValue& first, const QScriptValue& second); bool operator!=(const QScriptValue& first, const QScriptValue& second); +bool operator<(const QScriptValue& first, const QScriptValue& second); /// A program loaded from the network. class NetworkProgram : public Resource { diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 81f1840342..4dc1cdddce 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -41,6 +43,8 @@ static int streamedBytesReceived = 0; static int sharedObjectsCreated = 0; static int sharedObjectsDestroyed = 0; static int objectMutationsPerformed = 0; +static int scriptObjectsCreated = 0; +static int scriptMutationsPerformed = 0; static QByteArray createRandomBytes(int minimumSize, int maximumSize) { QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); @@ -79,6 +83,49 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() { return flags; } +static QScriptValue createRandomScriptValue() { + scriptObjectsCreated++; + switch (randIntInRange(0, 3)) { + case 0: + return QScriptValue(QScriptValue::NullValue); + + case 1: + return QScriptValue(randomBoolean()); + + case 2: + return QScriptValue(randFloat()); + + case 3: + default: + return QScriptValue(QString(createRandomBytes())); + + case 4: { + int length = randIntInRange(2, 6); + QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length); + for (int i = 0; i < length; i++) { + value.setProperty(i, createRandomScriptValue()); + } + return value; + } + case 5: { + QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject(); + if (randomBoolean()) { + value.setProperty("foo", createRandomScriptValue()); + } + if (randomBoolean()) { + value.setProperty("bar", createRandomScriptValue()); + } + if (randomBoolean()) { + value.setProperty("baz", createRandomScriptValue()); + } + if (randomBoolean()) { + value.setProperty("bong", createRandomScriptValue()); + } + return value; + } + } +} + static TestMessageC createRandomMessageC() { TestMessageC message; message.foo = randomBoolean(); @@ -86,6 +133,7 @@ static TestMessageC createRandomMessageC() { message.baz = randFloat(); message.bong.foo = createRandomBytes(); message.bong.baz = getRandomTestEnum(); + message.bizzle = createRandomScriptValue(); return message; } @@ -201,6 +249,7 @@ bool MetavoxelTests::run() { datagramsReceived << "with" << bytesReceived << "bytes"; qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; + qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed; qDebug(); qDebug() << "Running serialization tests..."; @@ -284,7 +333,7 @@ static QVariant createRandomMessage() { } static SharedObjectPointer mutate(const SharedObjectPointer& state) { - switch(randIntInRange(0, 3)) { + switch (randIntInRange(0, 4)) { case 0: { SharedObjectPointer newState = state->clone(true); static_cast(newState.data())->setFoo(randFloat()); @@ -303,6 +352,38 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { objectMutationsPerformed++; return newState; } + case 3: { + SharedObjectPointer newState = state->clone(true); + QScriptValue oldValue = static_cast(newState.data())->getBizzle(); + QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject(); + for (QScriptValueIterator it(oldValue); it.hasNext(); ) { + it.next(); + newValue.setProperty(it.scriptName(), it.value()); + } + switch (randIntInRange(0, 3)) { + case 0: { + QScriptValue oldArray = oldValue.property("foo"); + int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength); + for (int i = 0; i < oldLength; i++) { + newArray.setProperty(i, oldArray.property(i)); + } + newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue()); + break; + } + case 1: + newValue.setProperty("bar", QScriptValue(randFloat())); + break; + + default: + newValue.setProperty("baz", createRandomScriptValue()); + break; + } + static_cast(newState.data())->setBizzle(newValue); + scriptMutationsPerformed++; + objectMutationsPerformed++; + return newState; + } default: return state; } @@ -503,7 +584,10 @@ TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _foo(foo), _baz(baz), _bong(bong) { - sharedObjectsCreated++; + sharedObjectsCreated++; + + _bizzle = ScriptCache::getInstance()->getEngine()->newObject(); + _bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4)); } TestSharedObjectA::~TestSharedObjectA() { diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 345ea624df..ac9eda2659 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -16,6 +16,7 @@ #include #include +#include class SequencedTestMessage; @@ -96,7 +97,8 @@ class TestSharedObjectA : public SharedObject { Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged) Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz) Q_PROPERTY(TestFlags bong READ getBong WRITE setBong) - + Q_PROPERTY(QScriptValue bizzle READ getBizzle WRITE setBizzle) + public: enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM }; @@ -116,6 +118,9 @@ public: void setBong(TestFlags bong) { _bong = bong; } TestFlags getBong() const { return _bong; } + void setBizzle(const QScriptValue& bizzle) { _bizzle = bizzle; } + const QScriptValue& getBizzle() const { return _bizzle; } + signals: void fooChanged(float foo); @@ -125,6 +130,7 @@ private: float _foo; TestEnum _baz; TestFlags _bong; + QScriptValue _bizzle; }; DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum) @@ -204,6 +210,7 @@ class TestMessageC : STREAM public TestMessageA { public: STREAM TestMessageB bong; + STREAM QScriptValue bizzle; }; DECLARE_STREAMABLE_METATYPE(TestMessageC) From 3293ab788b7340c98fbc923d509b216445362e09 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 17:19:36 -0700 Subject: [PATCH 08/99] Fix for script object deltas. --- libraries/metavoxels/src/Bitstream.cpp | 3 ++- tests/metavoxels/src/MetavoxelTests.cpp | 15 +++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 7595580c33..d7e15a9dd6 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -411,7 +411,8 @@ void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& ref *this << value; } } else if (reference.isObject()) { - if (value.isObject()) { + if (value.isObject() && !(value.isArray() || value.isRegExp() || value.isDate() || + value.isQMetaObject() || value.isQObject() || value.isVariant())) { *this << false; for (QScriptValueIterator it(value); it.hasNext(); ) { it.next(); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 4dc1cdddce..c7e98ab0cf 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -83,9 +83,9 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() { return flags; } -static QScriptValue createRandomScriptValue() { +static QScriptValue createRandomScriptValue(bool complex = false) { scriptObjectsCreated++; - switch (randIntInRange(0, 3)) { + switch (randIntInRange(0, complex ? 5 : 2)) { case 0: return QScriptValue(QScriptValue::NullValue); @@ -96,7 +96,6 @@ static QScriptValue createRandomScriptValue() { return QScriptValue(randFloat()); case 3: - default: return QScriptValue(QString(createRandomBytes())); case 4: { @@ -107,7 +106,7 @@ static QScriptValue createRandomScriptValue() { } return value; } - case 5: { + default: { QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject(); if (randomBoolean()) { value.setProperty("foo", createRandomScriptValue()); @@ -133,7 +132,7 @@ static TestMessageC createRandomMessageC() { message.baz = randFloat(); message.bong.foo = createRandomBytes(); message.bong.baz = getRandomTestEnum(); - message.bizzle = createRandomScriptValue(); + message.bizzle = createRandomScriptValue(true); return message; } @@ -360,7 +359,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { it.next(); newValue.setProperty(it.scriptName(), it.value()); } - switch (randIntInRange(0, 3)) { + switch (randIntInRange(0, 2)) { case 0: { QScriptValue oldArray = oldValue.property("foo"); int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32(); @@ -368,7 +367,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { for (int i = 0; i < oldLength; i++) { newArray.setProperty(i, oldArray.property(i)); } - newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue()); + newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue(true)); break; } case 1: @@ -376,7 +375,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { break; default: - newValue.setProperty("baz", createRandomScriptValue()); + newValue.setProperty("baz", createRandomScriptValue(true)); break; } static_cast(newState.data())->setBizzle(newValue); From d1549b00abffccc56dfad0951302054a15e71521 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 17:22:44 -0700 Subject: [PATCH 09/99] Might as well include strings in the "simple" random script objects. --- tests/metavoxels/src/MetavoxelTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index c7e98ab0cf..6ec2331b14 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -85,7 +85,7 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() { static QScriptValue createRandomScriptValue(bool complex = false) { scriptObjectsCreated++; - switch (randIntInRange(0, complex ? 5 : 2)) { + switch (randIntInRange(0, complex ? 5 : 3)) { case 0: return QScriptValue(QScriptValue::NullValue); From b4e984086505814cbc426226777b74a113da8362 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Mon, 9 Jun 2014 23:15:45 -0400 Subject: [PATCH 10/99] Job #19766 BUG: Stop or reload all scripts crashes interface fix, part 2. Keep the scriptUrl internal to the ScriptEngine class and refer to it externally by the file name string. Change the ScriptEngine constructor to accept a filename QString, instead of a QUrl. Resolve constructor ambiguity in Particle, which creates anonymous ScriptEngine. --- interface/src/Application.cpp | 5 ++--- libraries/particles/src/Particle.cpp | 6 +++--- libraries/script-engine/src/ScriptEngine.cpp | 12 +++++------- libraries/script-engine/src/ScriptEngine.h | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fb821bff4..3ec5588c61 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3492,9 +3492,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - QUrl scriptUrl(scriptName); - scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); + scriptEngine = new ScriptEngine(scriptName, &_controllerScriptingInterface); + _scriptEnginesHash.insert(scriptName, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 59265c00dc..90e59b9422 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -873,7 +873,7 @@ void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObje void Particle::executeUpdateScripts() { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); + ScriptEngine engine(_script, QString("")); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitUpdate(); @@ -884,7 +884,7 @@ void Particle::executeUpdateScripts() { void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); + ScriptEngine engine(_script, QString("")); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); ParticleScriptObject otherParticleScriptable(other); @@ -896,7 +896,7 @@ void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetrati void Particle::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); + ScriptEngine engine(_script, QString("")); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a4aae61248..025f316d29 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -93,7 +93,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam { } -ScriptEngine::ScriptEngine(const QUrl& scriptURL, +ScriptEngine::ScriptEngine(const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface) : _scriptContents(), _isFinished(false), @@ -110,21 +110,19 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _scriptName(), - _fileNameString(), + _fileNameString(fileNameString), _quatLibrary(), _vec3Library(), _uuidLibrary(), _animationCache(this) { - QString scriptURLString = scriptURL.toString(); - _fileNameString = scriptURLString; - - QUrl url(scriptURL); + QUrl url(fileNameString); + QString scriptUrlString = url.toString(); // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { - url = QUrl::fromLocalFile(scriptURLString); + url = QUrl::fromLocalFile(scriptUrlString); } // ok, let's see if it's valid... and if so, load it diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index bf2ac40568..f88017515e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -40,7 +40,7 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10 class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(const QUrl& scriptURL, + ScriptEngine(const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ScriptEngine(const QString& scriptContents = NO_SCRIPT, From c9ba71aa015ef86d2a9fc89effbd77a18cbebb4a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 10 Jun 2014 09:38:38 -0700 Subject: [PATCH 11/99] fix for broken neck on some body models --- interface/src/avatar/FaceModel.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 10 ++++++++-- interface/src/avatar/SkeletonModel.h | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 601dad5563..c3f31ff6e7 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -30,7 +30,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } setTranslation(neckPosition); glm::quat neckParentRotation; - if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) { + if (!owningAvatar->getSkeletonModel().getNeckParentRotationFromDefaultOrientation(neckParentRotation)) { neckParentRotation = owningAvatar->getOrientation(); } setRotation(neckParentRotation); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1b1b2e032b..576545f115 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -434,7 +434,7 @@ bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition); } -bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const { +bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const { if (!isActive()) { return false; } @@ -442,7 +442,13 @@ bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const { if (geometry.neckJointIndex == -1) { return false; } - return getJointRotationInWorldFrame(geometry.joints.at(geometry.neckJointIndex).parentIndex, neckParentRotation); + int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex; + glm::quat worldFrameRotation; + if (getJointRotationInWorldFrame(parentIndex, worldFrameRotation)) { + neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation; + return true; + } + return false; } bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 086973807d..3b8e67df47 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -86,9 +86,9 @@ public: /// \return whether or not the neck was found bool getNeckPosition(glm::vec3& neckPosition) const; - /// Returns the rotation of the neck joint's parent. + /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found - bool getNeckParentRotation(glm::quat& neckRotation) const; + bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found From d84beee3e49699454f4b44414ec11910ccf12204 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 11:05:51 -0700 Subject: [PATCH 12/99] VoxelPacketProcessor -> OctreePacketProcessor added nodes bookkeeping in ReceivedPacketProcessor; added check in sendNack() to not send NACKs to nodes that have sent packets that are waiting in the message queue. --- interface/src/Application.cpp | 14 ++++++++++---- interface/src/Application.h | 8 ++++---- interface/src/DatagramProcessor.cpp | 4 ++-- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- ...etProcessor.cpp => OctreePacketProcessor.cpp} | 10 +++++----- ...PacketProcessor.h => OctreePacketProcessor.h} | 10 +++++----- libraries/networking/src/NetworkPacket.cpp | 16 ++++++++-------- libraries/networking/src/NetworkPacket.h | 8 ++++---- libraries/networking/src/PacketSender.cpp | 2 +- .../networking/src/ReceivedPacketProcessor.cpp | 10 ++++++---- .../networking/src/ReceivedPacketProcessor.h | 11 +++++++++-- 11 files changed, 56 insertions(+), 41 deletions(-) rename interface/src/voxels/{VoxelPacketProcessor.cpp => OctreePacketProcessor.cpp} (92%) rename interface/src/voxels/{VoxelPacketProcessor.h => OctreePacketProcessor.h} (76%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3cfec3190e..9f62742e6c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -158,7 +158,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _mousePressed(false), _audio(STARTUP_JITTER_SAMPLES), _enableProcessVoxelsThread(true), - _voxelProcessor(), + _octreeProcessor(), _voxelHideShowThread(&_voxels), _packetsPerSecond(0), _bytesPerSecond(0), @@ -418,7 +418,7 @@ Application::~Application() { _audio.thread()->quit(); _audio.thread()->wait(); - _voxelProcessor.terminate(); + _octreeProcessor.terminate(); _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _particleEditSender.terminate(); @@ -517,7 +517,7 @@ void Application::initializeGL() { qDebug( "init() complete."); // create thread for parsing of voxel data independent of the main network and rendering threads - _voxelProcessor.initialize(_enableProcessVoxelsThread); + _octreeProcessor.initialize(_enableProcessVoxelsThread); _voxelEditSender.initialize(_enableProcessVoxelsThread); _voxelHideShowThread.initialize(_enableProcessVoxelsThread); _particleEditSender.initialize(_enableProcessVoxelsThread); @@ -1884,7 +1884,7 @@ void Application::updateThreads(float deltaTime) { // parse voxel packets if (!_enableProcessVoxelsThread) { - _voxelProcessor.threadRoutine(); + _octreeProcessor.threadRoutine(); _voxelHideShowThread.threadRoutine(); _voxelEditSender.threadRoutine(); _particleEditSender.threadRoutine(); @@ -2125,6 +2125,12 @@ void Application::sendNack() { || node->getType() == NodeType::ModelServer) ) { + // if there are octree packets from this node that are waiting to be processed, + // don't send a NACK since the missing packets may be among those waiting packets. + if (_octreeProcessor.hasPacketsToProcessFrom(node)) { + continue; + } + QUuid nodeUUID = node->getUUID(); _octreeSceneStatsLock.lockForRead(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 170be43493..2889dcb301 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -88,7 +88,7 @@ #include "voxels/VoxelFade.h" #include "voxels/VoxelHideShowThread.h" #include "voxels/VoxelImporter.h" -#include "voxels/VoxelPacketProcessor.h" +#include "voxels/OctreePacketProcessor.h" #include "voxels/VoxelSystem.h" @@ -129,7 +129,7 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f; class Application : public QApplication { Q_OBJECT - friend class VoxelPacketProcessor; + friend class OctreePacketProcessor; friend class VoxelEditPacketSender; friend class DatagramProcessor; @@ -192,7 +192,7 @@ public: ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } VoxelTree* getVoxelTree() { return _voxels.getTree(); } - const VoxelPacketProcessor& getVoxelPacketProcessor() const { return _voxelProcessor; } + const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } ParticleTreeRenderer* getParticles() { return &_particles; } MetavoxelSystem* getMetavoxels() { return &_metavoxels; } ModelTreeRenderer* getModels() { return &_models; } @@ -533,7 +533,7 @@ private: Audio _audio; bool _enableProcessVoxelsThread; - VoxelPacketProcessor _voxelProcessor; + OctreePacketProcessor _octreeProcessor; VoxelHideShowThread _voxelHideShowThread; VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 56078c1a8d..cdd7e7ef0f 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -71,7 +71,7 @@ void DatagramProcessor::processDatagrams() { case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); + "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { @@ -92,7 +92,7 @@ void DatagramProcessor::processDatagrams() { if (matchedNode) { // add this packet to our list of voxel packets and process them on the voxel processing - application->_voxelProcessor.queueReceivedPacket(matchedNode, incomingPacket); + application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 49ec8ecddb..ec2d6c73b3 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -45,7 +45,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); Audio* audio = application->getAudio(); - const VoxelPacketProcessor& voxelPacketProcessor = application->getVoxelPacketProcessor(); + const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); @@ -200,7 +200,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; - int voxelPacketsToProcess = voxelPacketProcessor.packetsToProcessCount(); + int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); // Onscreen text about position, servers, etc Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess); // Bandwidth meter diff --git a/interface/src/voxels/VoxelPacketProcessor.cpp b/interface/src/voxels/OctreePacketProcessor.cpp similarity index 92% rename from interface/src/voxels/VoxelPacketProcessor.cpp rename to interface/src/voxels/OctreePacketProcessor.cpp index 095c378c04..66190a5689 100644 --- a/interface/src/voxels/VoxelPacketProcessor.cpp +++ b/interface/src/voxels/OctreePacketProcessor.cpp @@ -1,5 +1,5 @@ // -// VoxelPacketProcessor.cpp +// OctreePacketProcessor.cpp // interface/src/voxels // // Created by Brad Hefta-Gaub on 8/12/13. @@ -13,18 +13,18 @@ #include "Application.h" #include "Menu.h" -#include "VoxelPacketProcessor.h" +#include "OctreePacketProcessor.h" -void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { +void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "VoxelPacketProcessor::processPacket()"); + "OctreePacketProcessor::processPacket()"); QByteArray mutablePacket = packet; const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { - qDebug("VoxelPacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); + qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } ssize_t messageLength = mutablePacket.size(); diff --git a/interface/src/voxels/VoxelPacketProcessor.h b/interface/src/voxels/OctreePacketProcessor.h similarity index 76% rename from interface/src/voxels/VoxelPacketProcessor.h rename to interface/src/voxels/OctreePacketProcessor.h index 36456c5cc2..bdf25806f3 100644 --- a/interface/src/voxels/VoxelPacketProcessor.h +++ b/interface/src/voxels/OctreePacketProcessor.h @@ -1,5 +1,5 @@ // -// VoxelPacketProcessor.h +// OctreePacketProcessor.h // interface/src/voxels // // Created by Brad Hefta-Gaub on 8/12/13. @@ -9,16 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_VoxelPacketProcessor_h -#define hifi_VoxelPacketProcessor_h +#ifndef hifi_OctreePacketProcessor_h +#define hifi_OctreePacketProcessor_h #include /// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() -class VoxelPacketProcessor : public ReceivedPacketProcessor { +class OctreePacketProcessor : public ReceivedPacketProcessor { Q_OBJECT protected: virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); }; -#endif // hifi_VoxelPacketProcessor_h +#endif // hifi_OctreePacketProcessor_h diff --git a/libraries/networking/src/NetworkPacket.cpp b/libraries/networking/src/NetworkPacket.cpp index bf18aa9b37..f9d6c7ea7b 100644 --- a/libraries/networking/src/NetworkPacket.cpp +++ b/libraries/networking/src/NetworkPacket.cpp @@ -17,9 +17,9 @@ #include "NetworkPacket.h" -void NetworkPacket::copyContents(const SharedNodePointer& destinationNode, const QByteArray& packet) { +void NetworkPacket::copyContents(const SharedNodePointer& sendingNode, const QByteArray& packet) { if (packet.size() && packet.size() <= MAX_PACKET_SIZE) { - _destinationNode = destinationNode; + _sendingNode = sendingNode; _byteArray = packet; } else { qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size()); @@ -27,28 +27,28 @@ void NetworkPacket::copyContents(const SharedNodePointer& destinationNode, const } NetworkPacket::NetworkPacket(const NetworkPacket& packet) { - copyContents(packet.getDestinationNode(), packet.getByteArray()); + copyContents(packet.getSendingNode(), packet.getByteArray()); } -NetworkPacket::NetworkPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) { - copyContents(destinationNode, packet); +NetworkPacket::NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { + copyContents(sendingNode, packet); }; // copy assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) { - copyContents(other.getDestinationNode(), other.getByteArray()); + copyContents(other.getSendingNode(), other.getByteArray()); return *this; } #ifdef HAS_MOVE_SEMANTICS // move, same as copy, but other packet won't be used further NetworkPacket::NetworkPacket(NetworkPacket && packet) { - copyContents(packet.getDestinationNode(), packet.getByteArray()); + copyContents(packet.getSendingNode(), packet.getByteArray()); } // move assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) { - copyContents(other.getDestinationNode(), other.getByteArray()); + copyContents(other.getSendingNode(), other.getByteArray()); return *this; } #endif diff --git a/libraries/networking/src/NetworkPacket.h b/libraries/networking/src/NetworkPacket.h index 94ddf8d56e..deb4cb9fb9 100644 --- a/libraries/networking/src/NetworkPacket.h +++ b/libraries/networking/src/NetworkPacket.h @@ -34,15 +34,15 @@ public: NetworkPacket& operator= (NetworkPacket&& other); // move assignment #endif - NetworkPacket(const SharedNodePointer& destinationNode, const QByteArray& byteArray); + NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& byteArray); - const SharedNodePointer& getDestinationNode() const { return _destinationNode; } + const SharedNodePointer& getSendingNode() const { return _sendingNode; } const QByteArray& getByteArray() const { return _byteArray; } private: - void copyContents(const SharedNodePointer& destinationNode, const QByteArray& byteArray); + void copyContents(const SharedNodePointer& sendingNode, const QByteArray& byteArray); - SharedNodePointer _destinationNode; + SharedNodePointer _sendingNode; QByteArray _byteArray; }; diff --git a/libraries/networking/src/PacketSender.cpp b/libraries/networking/src/PacketSender.cpp index 5f7502a738..ae844d4f99 100644 --- a/libraries/networking/src/PacketSender.cpp +++ b/libraries/networking/src/PacketSender.cpp @@ -271,7 +271,7 @@ bool PacketSender::nonThreadedProcess() { unlock(); // send the packet through the NodeList... - NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getDestinationNode()); + NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getSendingNode()); packetsSentThisCall++; _packetsOverCheckInterval++; _totalPacketsSent++; diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index d54e165285..ea613082ce 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -17,13 +17,14 @@ void ReceivedPacketProcessor::terminating() { _hasPackets.wakeAll(); } -void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) { +void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { // Make sure our Node and NodeList knows we've heard from this node. - destinationNode->setLastHeardMicrostamp(usecTimestampNow()); + sendingNode->setLastHeardMicrostamp(usecTimestampNow()); - NetworkPacket networkPacket(destinationNode, packet); + NetworkPacket networkPacket(sendingNode, packet); lock(); _packets.push_back(networkPacket); + _nodePacketCounts[sendingNode->getUUID()]++; unlock(); // Make sure to wake our actual processing thread because we now have packets for it to process. @@ -42,8 +43,9 @@ bool ReceivedPacketProcessor::process() { NetworkPacket& packet = _packets.front(); // get the oldest packet NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us _packets.erase(_packets.begin()); // remove the oldest packet + _nodePacketCounts[temporary.getSendingNode()->getUUID()]--; unlock(); // let others add to the packets - processPacket(temporary.getDestinationNode(), temporary.getByteArray()); // process our temporary copy + processPacket(temporary.getSendingNode(), temporary.getByteArray()); // process our temporary copy } return isStillRunning(); // keep running till they terminate us } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index f8306b4896..dea7448af1 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -28,11 +28,16 @@ public: /// \param packetData pointer to received data /// \param ssize_t packetLength size of received data /// \thread network receive thread - void queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet); + void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); /// Are there received packets waiting to be processed bool hasPacketsToProcess() const { return _packets.size() > 0; } + /// Are there received packets waiting to be processed from a certain node + bool hasPacketsToProcessFrom(const SharedNodePointer& sendingNode) const { + return _nodePacketCounts[sendingNode->getUUID()] > 0; + } + /// How many received packets waiting are to be processed int packetsToProcessCount() const { return _packets.size(); } @@ -51,7 +56,9 @@ protected: private: - std::vector _packets; + QVector _packets; + QHash _nodePacketCounts; + QWaitCondition _hasPackets; QMutex _waitingOnPacketsMutex; }; From b96b6c9857086c811b50b1d231b67d0b1c87127b Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 12:24:47 -0700 Subject: [PATCH 13/99] added default constructor for NetworkPacket updated param comments in ReceivedPacketProcessor --- interface/src/Application.cpp | 2 +- libraries/networking/src/NetworkPacket.h | 1 + libraries/networking/src/ReceivedPacketProcessor.h | 6 ++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9f62742e6c..eab15ed678 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2095,7 +2095,7 @@ void Application::updateMyAvatar(float deltaTime) { } } - // sent a nack packet containing missing sequence numbers of received packets + // sent nack packets containing missing sequence numbers of received packets from nodes { quint64 now = usecTimestampNow(); quint64 sinceLastNack = now - _lastNackTime; diff --git a/libraries/networking/src/NetworkPacket.h b/libraries/networking/src/NetworkPacket.h index deb4cb9fb9..52e8a36712 100644 --- a/libraries/networking/src/NetworkPacket.h +++ b/libraries/networking/src/NetworkPacket.h @@ -26,6 +26,7 @@ /// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet class NetworkPacket { public: + NetworkPacket() { } NetworkPacket(const NetworkPacket& packet); // copy constructor NetworkPacket& operator= (const NetworkPacket& other); // copy assignment diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index dea7448af1..096005ffe7 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -43,10 +43,8 @@ public: protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. - /// \param sockaddr& senderAddress the address of the sender - /// \param packetData pointer to received data - /// \param ssize_t packetLength size of received data - /// \thread "this" individual processing thread + /// \param SharedNodePointer& sendingNode the node that sent this packet + /// \param QByteArray& the packet to be processed virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) = 0; /// Implements generic processing behavior for this thread. From 0ae7411bf0ccb59bb4340989968c77700fbe0e9b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 10 Jun 2014 13:37:53 -0700 Subject: [PATCH 14/99] fix some bugs in models --- interface/src/models/ModelTreeRenderer.cpp | 193 +++++++++++++-------- libraries/models/src/ModelTreeElement.cpp | 5 + 2 files changed, 124 insertions(+), 74 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 68bebd7b99..b20bdce8f5 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -57,21 +57,29 @@ void ModelTreeRenderer::render(RenderMode renderMode) { const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) { const FBXGeometry* result = NULL; + Model* model = getModel(modelItem); if (model) { result = &model->getGeometry()->getFBXGeometry(); - } return result; } Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; - + if (modelItem.isKnownID()) { if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) { model = _knownModelsItemModels[modelItem.getID()]; } else { + + // Make sure we only create new models on the thread that owns the ModelTreeRenderer + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem)); + return model; + } + model = new Model(); model->init(); model->setURL(QUrl(modelItem.getModelURL())); @@ -81,6 +89,13 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) { model = _unknownModelsItemModels[modelItem.getCreatorTokenID()]; } else { + // Make sure we only create new models on the thread that owns the ModelTreeRenderer + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem)); + return model; + } + model = new Model(); model->init(); model->setURL(QUrl(modelItem.getModelURL())); @@ -187,92 +202,122 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) if (drawAsModel) { glPushMatrix(); + { const float alpha = 1.0f; Model* model = getModel(modelItem); - - model->setScaleToFit(true, radius * 2.0f); - model->setSnapModelToCenter(true); - - // set the rotation - glm::quat rotation = modelItem.getModelRotation(); - model->setRotation(rotation); - - // set the position - model->setTranslation(position); - // handle animations.. - if (modelItem.hasAnimation()) { - if (!modelItem.jointsMapped()) { - QStringList modelJointNames = model->getJointNames(); - modelItem.mapJoints(modelJointNames); + if (model) { + model->setScaleToFit(true, radius * 2.0f); + model->setSnapModelToCenter(true); + + // set the rotation + glm::quat rotation = modelItem.getModelRotation(); + model->setRotation(rotation); + + // set the position + model->setTranslation(position); + + // handle animations.. + if (modelItem.hasAnimation()) { + if (!modelItem.jointsMapped()) { + QStringList modelJointNames = model->getJointNames(); + modelItem.mapJoints(modelJointNames); + } + + QVector frameData = modelItem.getAnimationFrame(); + for (int i = 0; i < frameData.size(); i++) { + model->setJointState(i, true, frameData[i]); + } + } + + // make sure to simulate so everything gets set up correctly for rendering + model->simulate(0.0f); + + // TODO: should we allow modelItems to have alpha on their models? + Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE + ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + + if (modelItem.getGlowLevel() > 0.0f) { + Glower glower(modelItem.getGlowLevel()); + + if (model->isActive()) { + model->render(alpha, modelRenderMode); + } else { + // if we couldn't get a model, then just draw a sphere + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutSolidSphere(radius, 15, 15); + glPopMatrix(); + } + } else { + if (model->isActive()) { + model->render(alpha, modelRenderMode); + } else { + // if we couldn't get a model, then just draw a sphere + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutSolidSphere(radius, 15, 15); + glPopMatrix(); + } } - QVector frameData = modelItem.getAnimationFrame(); - for (int i = 0; i < frameData.size(); i++) { - model->setJointState(i, true, frameData[i]); - } - } - - // make sure to simulate so everything gets set up correctly for rendering - model->simulate(0.0f); + if (!isShadowMode && displayModelBounds) { - // TODO: should we allow modelItems to have alpha on their models? - Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE - ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; - - if (modelItem.getGlowLevel() > 0.0f) { - Glower glower(modelItem.getGlowLevel()); - model->render(alpha, modelRenderMode); + glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum; + glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum; + glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; + + float width = unRotatedExtents.x; + float height = unRotatedExtents.y; + float depth = unRotatedExtents.z; + + Extents rotatedExtents = model->getUnscaledMeshExtents(); + calculateRotatedExtents(rotatedExtents, rotation); + + glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum; + + const glm::vec3& modelScale = model->getScale(); + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + + // draw the orignal bounding cube + glColor4f(1.0f, 1.0f, 0.0f, 1.0f); + glutWireCube(size); + + // draw the rotated bounding cube + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + glPushMatrix(); + glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z); + glutWireCube(1.0); + glPopMatrix(); + + // draw the model relative bounding box + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z); + glColor3f(0.0f, 1.0f, 0.0f); + glutWireCube(1.0); + + glPopMatrix(); + + } } else { - model->render(alpha, modelRenderMode); - } - - if (!isShadowMode && displayModelBounds) { - - glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum; - glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum; - glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; - - float width = unRotatedExtents.x; - float height = unRotatedExtents.y; - float depth = unRotatedExtents.z; - - Extents rotatedExtents = model->getUnscaledMeshExtents(); - calculateRotatedExtents(rotatedExtents, rotation); - - glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum; - - const glm::vec3& modelScale = model->getScale(); - + // if we couldn't get a model, then just draw a sphere + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); glPushMatrix(); glTranslatef(position.x, position.y, position.z); - - // draw the orignal bounding cube - glColor4f(1.0f, 1.0f, 0.0f, 1.0f); - glutWireCube(size); - - // draw the rotated bounding cube - glColor4f(0.0f, 0.0f, 1.0f, 1.0f); - glPushMatrix(); - glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z); - glutWireCube(1.0); - glPopMatrix(); - - // draw the model relative bounding box - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z); - glColor3f(0.0f, 1.0f, 0.0f); - glutWireCube(1.0); - + glutSolidSphere(radius, 15, 15); glPopMatrix(); - } - + } glPopMatrix(); } else { - glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + //glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glColor3f(1.0f, 0.0f, 0.0f); glPushMatrix(); glTranslatef(position.x, position.y, position.z); glutSolidSphere(radius, 15, 15); diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index b0c7e125b4..75b9670d0f 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -186,6 +186,11 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons if (fbxGeometry && fbxGeometry->meshExtents.isValid()) { Extents extents = fbxGeometry->meshExtents; + // NOTE: If the model has a bad mesh, then extents will be 0,0,0 & 0,0,0 + if (extents.minimum == extents.maximum && extents.minimum == glm::vec3(0,0,0)) { + extents.maximum = glm::vec3(1.0f,1.0f,1.0f); // in this case we will simulate the unit cube + } + // NOTE: these extents are model space, so we need to scale and center them accordingly // size is our "target size in world space" // we need to set our model scale so that the extents of the mesh, fit in a cube that size... From 39e3deb9b950e0976c33fc9deca50b441daa695f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Jun 2014 15:03:07 -0700 Subject: [PATCH 15/99] Add menu item that toggles VR display mode Toggles the display mode if a Rift is found connected at program start. --- interface/src/Application.cpp | 3 +++ interface/src/Application.h | 1 + interface/src/Menu.cpp | 5 +++++ interface/src/Menu.h | 1 + interface/src/devices/OculusManager.cpp | 8 ++++++++ interface/src/devices/OculusManager.h | 2 +- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3cfec3190e..3075d6ccaf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1369,6 +1369,9 @@ void Application::setEnable3DTVMode(bool enable3DTVMode) { resizeGL(_glWidget->width(),_glWidget->height()); } +void Application::setEnableVRMode(bool enableVRMode) { + resizeGL(_glWidget->width(), _glWidget->height()); +} void Application::setRenderVoxels(bool voxelRender) { _voxelEditSender.setShouldSend(voxelRender); diff --git a/interface/src/Application.h b/interface/src/Application.h index 170be43493..f164c28be1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -345,6 +345,7 @@ private slots: void setFullscreen(bool fullscreen); void setEnable3DTVMode(bool enable3DTVMode); + void setEnableVRMode(bool enableVRMode); void cameraMenuChanged(); glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..d59a4b23e1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -253,6 +253,11 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, appInstance, SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, + false, + appInstance, + SLOT(setEnableVRMode(bool))); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false, appInstance, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..b39aefb0ba 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -334,6 +334,7 @@ namespace MenuOption { const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; const QString Enable3DTVMode = "Enable 3DTV Mode"; + const QString EnableVRMode = "Enable VR Mode"; const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing"; const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing"; const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing"; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index fe8cc37168..b2ee4e8c18 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -72,6 +72,14 @@ void OculusManager::connect() { #endif } +bool OculusManager::isConnected() { +#ifdef HAVE_LIBOVR + return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode); +#else + return false; +#endif +} + void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) { #ifdef HAVE_LIBOVR _stereoConfig.SetFullViewport(Viewport(0, 0, screenWidth, screenHeight)); diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 6376df05ca..caff38a6b0 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -27,7 +27,7 @@ class OculusManager { public: static void connect(); - static bool isConnected() { return _isConnected; } + static bool isConnected(); static void configureCamera(Camera& camera, int screenWidth, int screenHeight); From 5ab271330e0d9a768adc1685e069b5a7560febd1 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 15:35:48 -0700 Subject: [PATCH 16/99] moved sendNack() into packetDistributor() ...for the purpose of enforcing packets sent per interval in OctreeSendThread. Corrected mistake in keeping track of number of special packets sent where sendSpecialPacket() was assumed to only send one packet per call. --- assignment-client/src/models/ModelServer.cpp | 4 +- assignment-client/src/models/ModelServer.h | 2 +- .../src/octree/OctreeSendThread.cpp | 49 +++++++++---------- .../src/octree/OctreeSendThread.h | 3 -- assignment-client/src/octree/OctreeServer.h | 2 +- .../src/particles/ParticleServer.cpp | 4 +- .../src/particles/ParticleServer.h | 2 +- assignment-client/src/voxels/VoxelServer.cpp | 3 +- assignment-client/src/voxels/VoxelServer.h | 2 +- 9 files changed, 34 insertions(+), 37 deletions(-) diff --git a/assignment-client/src/models/ModelServer.cpp b/assignment-client/src/models/ModelServer.cpp index 07359f001a..ae6ffaf969 100644 --- a/assignment-client/src/models/ModelServer.cpp +++ b/assignment-client/src/models/ModelServer.cpp @@ -86,7 +86,7 @@ bool ModelServer::hasSpecialPacketToSend(const SharedNodePointer& node) { return shouldSendDeletedModels; } -int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { +int ModelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { unsigned char outputBuffer[MAX_PACKET_SIZE]; size_t packetLength = 0; @@ -99,6 +99,7 @@ int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP bool hasMoreToSend = true; // TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 models? + packetsSent = 0; while (hasMoreToSend) { hasMoreToSend = tree->encodeModelsDeletedSince(queryNode->getSequenceNumber(), deletedModelsSentAt, outputBuffer, MAX_PACKET_SIZE, packetLength); @@ -107,6 +108,7 @@ int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node)); queryNode->packetSent(outputBuffer, packetLength); + packetsSent++; } nodeData->setLastDeletedModelsSentAt(deletePacketSentAt); diff --git a/assignment-client/src/models/ModelServer.h b/assignment-client/src/models/ModelServer.h index 7e7f239f2a..38acc7f1e1 100644 --- a/assignment-client/src/models/ModelServer.h +++ b/assignment-client/src/models/ModelServer.h @@ -37,7 +37,7 @@ public: // subclass may implement these method virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node); + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 30e3011ae8..bc0e84eea9 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -85,7 +85,6 @@ bool OctreeSendThread::process() { if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); packetDistributor(nodeData, viewFrustumChanged); - resendNackedPackets(nodeData); } } } @@ -281,26 +280,6 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes return packetsSent; } -int OctreeSendThread::resendNackedPackets(OctreeQueryNode* nodeData) { - - const int MAX_PACKETS_RESEND = 10; - int packetsSent = 0; - - const QByteArray* packet; - while (nodeData->hasNextNackedPacket() && packetsSent < MAX_PACKETS_RESEND) { - packet = nodeData->getNextNackedPacket(); - if (packet) { - NodeList::getInstance()->writeDatagram(*packet, _node); - packetsSent++; - - _totalBytes += packet->size(); - _totalPackets++; - _totalWastedBytes += MAX_PACKET_SIZE - packet->size(); - } - } - return packetsSent; -} - /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) { @@ -311,6 +290,11 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus return 0; } + // calculate max number of packets that can be sent during this interval + int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); + int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); + + int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; @@ -408,9 +392,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus //quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; //quint64 startCompressCalls = OctreePacketData::getCompressContentCalls(); - int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); - int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - int extraPackingAttempts = 0; bool completedScene = false; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) { @@ -581,12 +562,26 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) { - trueBytesSent += _myServer->sendSpecialPacket(nodeData, _node); + int specialPacketsSent; + trueBytesSent += _myServer->sendSpecialPacket(_node, nodeData, specialPacketsSent); nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed - truePacketsSent++; - packetsSentThisInterval++; + truePacketsSent += specialPacketsSent; + packetsSentThisInterval += specialPacketsSent; } + // Re-send packets that were nacked by the client + while (nodeData->hasNextNackedPacket() && packetsSentThisInterval < maxPacketsPerInterval) { + const QByteArray* packet = nodeData->getNextNackedPacket(); + if (packet) { + NodeList::getInstance()->writeDatagram(*packet, _node); + truePacketsSent++; + packetsSentThisInterval++; + + _totalBytes += packet->size(); + _totalPackets++; + _totalWastedBytes += MAX_PACKET_SIZE - packet->size(); + } + } quint64 end = usecTimestampNow(); int elapsedmsec = (end - start)/USECS_PER_MSEC; diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index e7599ebcd2..d8eed27802 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -55,9 +55,6 @@ private: int _nodeMissingCount; bool _isShuttingDown; - - int resendNackedPackets(OctreeQueryNode* nodeData); - }; #endif // hifi_OctreeSendThread_h diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index f0db93feb3..5595d139be 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -72,7 +72,7 @@ public: // subclass may implement these method virtual void beforeRun() { }; virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; } - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { return 0; } + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; } static void attachQueryNodeToNode(Node* newNode); diff --git a/assignment-client/src/particles/ParticleServer.cpp b/assignment-client/src/particles/ParticleServer.cpp index e7a0f75dfd..674d22145f 100644 --- a/assignment-client/src/particles/ParticleServer.cpp +++ b/assignment-client/src/particles/ParticleServer.cpp @@ -86,7 +86,7 @@ bool ParticleServer::hasSpecialPacketToSend(const SharedNodePointer& node) { return shouldSendDeletedParticles; } -int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { +int ParticleServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { unsigned char outputBuffer[MAX_PACKET_SIZE]; size_t packetLength = 0; @@ -99,6 +99,7 @@ int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNo bool hasMoreToSend = true; // TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles? + packetsSent = 0; while (hasMoreToSend) { hasMoreToSend = tree->encodeParticlesDeletedSince(queryNode->getSequenceNumber(), deletedParticlesSentAt, outputBuffer, MAX_PACKET_SIZE, packetLength); @@ -107,6 +108,7 @@ int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNo NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node)); queryNode->packetSent(outputBuffer, packetLength); + packetsSent++; } nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt); diff --git a/assignment-client/src/particles/ParticleServer.h b/assignment-client/src/particles/ParticleServer.h index 3066c5fa98..d444368a9d 100644 --- a/assignment-client/src/particles/ParticleServer.h +++ b/assignment-client/src/particles/ParticleServer.h @@ -37,7 +37,7 @@ public: // subclass may implement these method virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node); + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); virtual void particleCreated(const Particle& newParticle, const SharedNodePointer& senderNode); diff --git a/assignment-client/src/voxels/VoxelServer.cpp b/assignment-client/src/voxels/VoxelServer.cpp index 34b01f529a..b021ddd9f6 100644 --- a/assignment-client/src/voxels/VoxelServer.cpp +++ b/assignment-client/src/voxels/VoxelServer.cpp @@ -40,7 +40,7 @@ bool VoxelServer::hasSpecialPacketToSend(const SharedNodePointer& node) { return shouldSendEnvironments; } -int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { +int VoxelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { unsigned char* copyAt = _tempOutputBuffer; @@ -76,6 +76,7 @@ int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP NodeList::getInstance()->writeDatagram((char*) _tempOutputBuffer, envPacketLength, SharedNodePointer(node)); queryNode->packetSent(_tempOutputBuffer, envPacketLength); + packetsSent = 1; return envPacketLength; } diff --git a/assignment-client/src/voxels/VoxelServer.h b/assignment-client/src/voxels/VoxelServer.h index 4e04c48cfd..b13f83b65f 100644 --- a/assignment-client/src/voxels/VoxelServer.h +++ b/assignment-client/src/voxels/VoxelServer.h @@ -46,7 +46,7 @@ public: // subclass may implement these method virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node); + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); private: bool _sendEnvironments; From c6a9a8924dbb847e7bacdac8789c3a484cf8b07e Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 16:43:53 -0700 Subject: [PATCH 17/99] changed NetworkPacket::_sendingNode to ::_node --- .../src/octree/OctreeSendThread.cpp | 1 - libraries/networking/src/NetworkPacket.cpp | 16 ++++++++-------- libraries/networking/src/NetworkPacket.h | 8 ++++---- libraries/networking/src/PacketSender.cpp | 2 +- .../networking/src/ReceivedPacketProcessor.cpp | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index bc0e84eea9..cb149b1d96 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -294,7 +294,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; diff --git a/libraries/networking/src/NetworkPacket.cpp b/libraries/networking/src/NetworkPacket.cpp index f9d6c7ea7b..a8110847e1 100644 --- a/libraries/networking/src/NetworkPacket.cpp +++ b/libraries/networking/src/NetworkPacket.cpp @@ -17,9 +17,9 @@ #include "NetworkPacket.h" -void NetworkPacket::copyContents(const SharedNodePointer& sendingNode, const QByteArray& packet) { +void NetworkPacket::copyContents(const SharedNodePointer& node, const QByteArray& packet) { if (packet.size() && packet.size() <= MAX_PACKET_SIZE) { - _sendingNode = sendingNode; + _node = node; _byteArray = packet; } else { qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size()); @@ -27,28 +27,28 @@ void NetworkPacket::copyContents(const SharedNodePointer& sendingNode, const QBy } NetworkPacket::NetworkPacket(const NetworkPacket& packet) { - copyContents(packet.getSendingNode(), packet.getByteArray()); + copyContents(packet.getNode(), packet.getByteArray()); } -NetworkPacket::NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { - copyContents(sendingNode, packet); +NetworkPacket::NetworkPacket(const SharedNodePointer& node, const QByteArray& packet) { + copyContents(node, packet); }; // copy assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) { - copyContents(other.getSendingNode(), other.getByteArray()); + copyContents(other.getNode(), other.getByteArray()); return *this; } #ifdef HAS_MOVE_SEMANTICS // move, same as copy, but other packet won't be used further NetworkPacket::NetworkPacket(NetworkPacket && packet) { - copyContents(packet.getSendingNode(), packet.getByteArray()); + copyContents(packet.getNode(), packet.getByteArray()); } // move assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) { - copyContents(other.getSendingNode(), other.getByteArray()); + copyContents(other.getNode(), other.getByteArray()); return *this; } #endif diff --git a/libraries/networking/src/NetworkPacket.h b/libraries/networking/src/NetworkPacket.h index 52e8a36712..0be35f9fff 100644 --- a/libraries/networking/src/NetworkPacket.h +++ b/libraries/networking/src/NetworkPacket.h @@ -35,15 +35,15 @@ public: NetworkPacket& operator= (NetworkPacket&& other); // move assignment #endif - NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& byteArray); + NetworkPacket(const SharedNodePointer& node, const QByteArray& byteArray); - const SharedNodePointer& getSendingNode() const { return _sendingNode; } + const SharedNodePointer& getNode() const { return _node; } const QByteArray& getByteArray() const { return _byteArray; } private: - void copyContents(const SharedNodePointer& sendingNode, const QByteArray& byteArray); + void copyContents(const SharedNodePointer& node, const QByteArray& byteArray); - SharedNodePointer _sendingNode; + SharedNodePointer _node; QByteArray _byteArray; }; diff --git a/libraries/networking/src/PacketSender.cpp b/libraries/networking/src/PacketSender.cpp index ae844d4f99..3edfc47c04 100644 --- a/libraries/networking/src/PacketSender.cpp +++ b/libraries/networking/src/PacketSender.cpp @@ -271,7 +271,7 @@ bool PacketSender::nonThreadedProcess() { unlock(); // send the packet through the NodeList... - NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getSendingNode()); + NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getNode()); packetsSentThisCall++; _packetsOverCheckInterval++; _totalPacketsSent++; diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index ea613082ce..46f1515016 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -43,9 +43,9 @@ bool ReceivedPacketProcessor::process() { NetworkPacket& packet = _packets.front(); // get the oldest packet NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us _packets.erase(_packets.begin()); // remove the oldest packet - _nodePacketCounts[temporary.getSendingNode()->getUUID()]--; + _nodePacketCounts[temporary.getNode()->getUUID()]--; unlock(); // let others add to the packets - processPacket(temporary.getSendingNode(), temporary.getByteArray()); // process our temporary copy + processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy } return isStillRunning(); // keep running till they terminate us } From 69899f4d37bfa28bf1f29f48a0bd3121d6a6a46b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 10 Jun 2014 16:53:00 -0700 Subject: [PATCH 18/99] Edging towards generic container types. --- libraries/metavoxels/src/Bitstream.cpp | 95 ++++++++++++++++++++++---- libraries/metavoxels/src/Bitstream.h | 68 +++++++++++++++--- 2 files changed, 140 insertions(+), 23 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d7e15a9dd6..ca6cd5dd83 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -44,6 +44,9 @@ static int quatStreamer = Bitstream::registerTypeStreamer(qMetaTypeId static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); +static int genericValueStreamer = Bitstream::registerTypeStreamer( + qRegisterMetaType(), new GenericTypeStreamer()); + IDStreamer::IDStreamer(Bitstream& stream) : _stream(stream), _bits(1) { @@ -92,7 +95,10 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta } int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { - streamer->setType(type); + streamer->_type = type; + if (!streamer->_self) { + streamer->_self = TypeStreamerPointer(streamer); + } getTypeStreamers().insert(type, streamer); return 0; } @@ -109,12 +115,13 @@ QList Bitstream::getMetaObjectSubClasses(const QMetaObject* return getMetaObjectSubClasses().values(metaObject); } -Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, QObject* parent) : +Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, GenericsMode genericsMode, QObject* parent) : QObject(parent), _underlying(underlying), _byte(0), _position(0), _metadataType(metadataType), + _genericsMode(genericsMode), _metaObjectStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), @@ -751,7 +758,7 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { - _typeStreamerStreamer << streamer; + _typeStreamerStreamer << streamer->getStreamerToWrite(value); streamer->write(*this, value); } else { qWarning() << "Non-streamable type: " << value.typeName() << "\n"; @@ -793,6 +800,16 @@ Bitstream& Bitstream::operator>>(OwnedAttributeValue& attributeValue) { return *this; } +Bitstream& Bitstream::operator<<(const GenericValue& value) { + value.getStreamer()->write(*this, value.getValue()); + return *this; +} + +Bitstream& Bitstream::operator>>(GenericValue& value) { + value = GenericValue(); + return *this; +} + Bitstream& Bitstream::operator<<(const QObject* object) { if (!object) { _metaObjectStreamer << NULL; @@ -1232,20 +1249,26 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { streamer = getEnumStreamersByName().value(typeName); } } - if (!streamer) { - qWarning() << "Unknown type name: " << typeName << "\n"; - } if (_metadataType == NO_METADATA) { + if (!streamer) { + qWarning() << "Unknown type name:" << typeName; + } reader = TypeReader(typeName, streamer); return *this; } int type; *this >> type; + if (type == TypeReader::SIMPLE_TYPE) { + if (!streamer) { + qWarning() << "Unknown type name:" << typeName; + } + reader = TypeReader(typeName, streamer); + return *this; + } + if (_genericsMode == ALL_GENERICS) { + streamer = NULL; + } switch (type) { - case TypeReader::SIMPLE_TYPE: - reader = TypeReader(typeName, streamer); - return *this; - case TypeReader::ENUM_TYPE: { if (_metadataType == FULL_METADATA) { int keyCount; @@ -1910,6 +1933,38 @@ const char* TypeStreamer::getName() const { return QMetaType::typeName(_type); } +const TypeStreamer* TypeStreamer::getStreamerToWrite(const QVariant& value) const { + return this; +} + +bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const { + return first == second; +} + +void TypeStreamer::write(Bitstream& out, const QVariant& value) const { + // nothing by default +} + +QVariant TypeStreamer::read(Bitstream& in) const { + return QVariant(); +} + +void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + // nothing by default +} + +void TypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + value = reference; +} + +void TypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + // nothing by default +} + +void TypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + value = reference; +} + void TypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { // nothing by default } @@ -1993,15 +2048,17 @@ EnumTypeStreamer::EnumTypeStreamer(const QMetaObject* metaObject, const char* na _name(QByteArray(metaObject->className()) + "::" + name), _bits(-1) { - setType(QMetaType::Int); + _type = QMetaType::Int; + _self = TypeStreamerPointer(this); } EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) : _name(QByteArray(metaEnum.scope()) + "::" + metaEnum.name()), _metaEnum(metaEnum), _bits(-1) { - - setType(QMetaType::Int); + + _type = QMetaType::Int; + _self = TypeStreamerPointer(this); } const char* EnumTypeStreamer::getName() const { @@ -2094,3 +2151,15 @@ void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash().getStreamer().data(); +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index d05f6574c0..dadfe52911 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -36,6 +36,7 @@ class Attribute; class AttributeValue; class Bitstream; class FieldReader; +class GenericValue; class ObjectReader; class OwnedAttributeValue; class PropertyReader; @@ -232,8 +233,17 @@ public: enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA }; + enum GenericsMode { NO_GENERICS, FALLBACK_GENERICS, ALL_GENERICS }; + /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. - Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, QObject* parent = NULL); + /// \param metadataType the metadata type, which determines the amount of version-resiliency: with no metadata, all types + /// must match exactly; with hash metadata, any types which do not match exactly will be ignored; with full metadata, + /// fields (and enum values, etc.) will be remapped by name + /// \param genericsMode the generics mode, which determines which types will be replaced by generic equivalents: with + /// no generics, no generics will be created; with fallback generics, generics will be created for any unknown types; with + /// all generics, generics will be created for all non-simple types + Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, + GenericsMode = NO_GENERICS, QObject* parent = NULL); /// Substitutes the supplied metaobject for the given class name's default mapping. void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject); @@ -364,6 +374,9 @@ public: Bitstream& operator<<(const AttributeValue& attributeValue); Bitstream& operator>>(OwnedAttributeValue& attributeValue); + Bitstream& operator<<(const GenericValue& value); + Bitstream& operator>>(GenericValue& value); + template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); @@ -429,6 +442,7 @@ private: int _position; MetadataType _metadataType; + GenericsMode _genericsMode; RepeatedValueStreamer _metaObjectStreamer; RepeatedValueStreamer _typeStreamerStreamer; @@ -890,27 +904,32 @@ Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); +typedef QSharedPointer TypeStreamerPointer; + /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: virtual ~TypeStreamer(); - void setType(int type) { _type = type; } int getType() const { return _type; } + const TypeStreamerPointer& getSelf() const { return _self; } + virtual const char* getName() const; - virtual bool equal(const QVariant& first, const QVariant& second) const = 0; + virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; - virtual void write(Bitstream& out, const QVariant& value) const = 0; - virtual QVariant read(Bitstream& in) const = 0; + virtual bool equal(const QVariant& first, const QVariant& second) const; + + virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; - virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0; - virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0; + virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; + virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; - virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0; - virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0; + virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; + virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; virtual void setEnumValue(QVariant& object, int value, const QHash& mappings) const; @@ -937,9 +956,12 @@ public: virtual QVariant getValue(const QVariant& object, int index) const; virtual void setValue(QVariant& object, int index, const QVariant& value) const; -private: +protected: + + friend class Bitstream; int _type; + TypeStreamerPointer _self; }; QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer); @@ -992,6 +1014,32 @@ private: int _bits; }; +/// Contains a value along with a pointer to its streamer. +class GenericValue { +public: + + GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); + + const TypeStreamerPointer& getStreamer() const { return _streamer; } + const QVariant& getValue() const { return _value; } + + bool operator==(const GenericValue& other) const; + +private: + + TypeStreamerPointer _streamer; + QVariant _value; +}; + +Q_DECLARE_METATYPE(GenericValue) + +/// A streamer class for generic values. +class GenericTypeStreamer : public SimpleTypeStreamer { +public: + + virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; +}; + /// A streamer for types compiled by mtc. template class StreamableTypeStreamer : public SimpleTypeStreamer { public: From faa5b1d2f253e3964cc8781fe821cb283d7b171a Mon Sep 17 00:00:00 2001 From: Dev5 Date: Tue, 10 Jun 2014 17:18:32 -0700 Subject: [PATCH 19/99] Added simple tree placement script that has a UI similar to edit voxels. It grows big trees, dont grow near something important. --- examples/growTrees.js | 837 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 examples/growTrees.js diff --git a/examples/growTrees.js b/examples/growTrees.js new file mode 100644 index 0000000000..d853804e77 --- /dev/null +++ b/examples/growTrees.js @@ -0,0 +1,837 @@ +// +// growPlants.js +// examples +// +// Created by Benjamin Arnold on May 29, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// This sample script allows the user to grow different types of plants on the voxels +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting +var previewLineWidth = 2.0; + +var voxelSize = 1; +var windowDimensions = Controller.getViewportDimensions(); +var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; + +var MAX_VOXEL_SCALE_POWER = 5; +var MIN_VOXEL_SCALE_POWER = -8; +var MAX_VOXEL_SCALE = Math.pow(2.0, MAX_VOXEL_SCALE_POWER); +var MIN_VOXEL_SCALE = Math.pow(2.0, MIN_VOXEL_SCALE_POWER); + + +var linePreviewTop = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var linePreviewBottom = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var linePreviewLeft = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var linePreviewRight = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + + +var UIColor = { red: 0, green: 160, blue: 0}; +var activeUIColor = { red: 0, green: 255, blue: 0}; + +var toolHeight = 50; +var toolWidth = 50; + +var editToolsOn = true; + +var voxelToolSelected = false; + +var scaleSelectorWidth = 144; +var scaleSelectorHeight = 37; + +var scaleSelectorX = windowDimensions.x / 5.0; +var scaleSelectorY = windowDimensions.y - scaleSelectorHeight; + +var voxelTool = Overlays.addOverlay("image", { + x: scaleSelectorX + scaleSelectorWidth + 1, y: windowDimensions.y - toolHeight, width: toolWidth, height: toolHeight, + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "voxel-tool.svg", + visible: editToolsOn, + color: UIColor, + alpha: 0.9 + }); + +var copyScale = true; +function ScaleSelector() { + this.x = scaleSelectorX; + this.y = scaleSelectorY; + this.width = scaleSelectorWidth; + this.height = scaleSelectorHeight; + + this.displayPower = false; + this.scale = 1.0; + this.power = 0; + + this.FIRST_PART = this.width * 40.0 / 100.0; + this.SECOND_PART = this.width * 37.0 / 100.0; + + this.buttonsOverlay = Overlays.addOverlay("image", { + x: this.x, y: this.y, + width: this.width, height: this.height, + //subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "voxel-size-selector.svg", + alpha: 0.9, + visible: editToolsOn, + color: activeUIColor + }); + this.textOverlay = Overlays.addOverlay("text", { + x: this.x + this.FIRST_PART, y: this.y, + width: this.SECOND_PART, height: this.height, + topMargin: 13, + text: this.scale.toString(), + alpha: 0.0, + visible: editToolsOn, + color: activeUIColor + }); + this.powerOverlay = Overlays.addOverlay("text", { + x: this.x + this.FIRST_PART, y: this.y, + width: this.SECOND_PART, height: this.height, + leftMargin: 28, + text: this.power.toString(), + alpha: 0.0, + visible: false, + color: activeUIColor + }); + this.setScale = function(scale) { + if (scale > MAX_VOXEL_SCALE) { + scale = MAX_VOXEL_SCALE; + } + if (scale < MIN_VOXEL_SCALE) { + scale = MIN_VOXEL_SCALE; + } + + this.scale = scale; + this.power = Math.floor(Math.log(scale) / Math.log(2)); + this.update(); + } + + this.show = function(doShow) { + Overlays.editOverlay(this.buttonsOverlay, {visible: doShow}); + Overlays.editOverlay(this.textOverlay, {visible: doShow}); + Overlays.editOverlay(this.powerOverlay, {visible: doShow && this.displayPower}); + } + + this.move = function() { + this.x = swatchesX + swatchesWidth; + this.y = swatchesY; + + Overlays.editOverlay(this.buttonsOverlay, { + x: this.x, y: this.y, + }); + Overlays.editOverlay(this.textOverlay, { + x: this.x + this.FIRST_PART, y: this.y, + }); + Overlays.editOverlay(this.powerOverlay, { + x: this.x + this.FIRST_PART, y: this.y, + }); + } + + + this.switchDisplay = function() { + this.displayPower = !this.displayPower; + + if (this.displayPower) { + Overlays.editOverlay(this.textOverlay, { + leftMargin: 18, + text: "2" + }); + Overlays.editOverlay(this.powerOverlay, { + text: this.power.toString(), + visible: editToolsOn + }); + } else { + Overlays.editOverlay(this.textOverlay, { + leftMargin: 13, + text: this.scale.toString() + }); + Overlays.editOverlay(this.powerOverlay, { + visible: false + }); + } + } + + this.update = function() { + if (this.displayPower) { + Overlays.editOverlay(this.powerOverlay, {text: this.power.toString()}); + } else { + Overlays.editOverlay(this.textOverlay, {text: this.scale.toString()}); + } + } + + this.incrementScale = function() { + copyScale = false; + if (this.power < MAX_VOXEL_SCALE_POWER) { + ++this.power; + this.scale *= 2.0; + this.update(); + } + } + + this.decrementScale = function() { + copyScale = false; + if (MIN_VOXEL_SCALE_POWER < this.power) { + --this.power; + this.scale /= 2.0; + this.update(); + } + } + + this.clicked = function(x, y) { + if (this.x < x && x < this.x + this.width && + this.y < y && y < this.y + this.height) { + + if (x < this.x + this.FIRST_PART) { + this.decrementScale(); + } else if (x < this.x + this.FIRST_PART + this.SECOND_PART) { + this.switchDisplay(); + } else { + this.incrementScale(); + } + return true; + } + return false; + } + + this.cleanup = function() { + Overlays.deleteOverlay(this.buttonsOverlay); + Overlays.deleteOverlay(this.textOverlay); + Overlays.deleteOverlay(this.powerOverlay); + } + +} +var scaleSelector = new ScaleSelector(); + + +function calculateVoxelFromIntersection(intersection, operation) { + + var resultVoxel; + + var x; + var y; + var z; + + // if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest + // ancestor voxel of our target size that contains our intersected voxel. + if (voxelSize > intersection.voxel.s) { + x = Math.floor(intersection.voxel.x / voxelSize) * voxelSize; + y = Math.floor(intersection.voxel.y / voxelSize) * voxelSize; + z = Math.floor(intersection.voxel.z / voxelSize) * voxelSize; + } else { + // otherwise, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of. + // if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result + // in the subvoxel that the intersection point falls in, if the target voxelSize matches the intersecting + // voxel this still works and results in returning the intersecting voxel which is what we want + var adjustToCenter = Vec3.multiply(Voxels.getFaceVector(intersection.face), (voxelSize * -0.5)); + + var centerOfIntersectingVoxel = Vec3.sum(intersection.intersection, adjustToCenter); + x = Math.floor(centerOfIntersectingVoxel.x / voxelSize) * voxelSize; + y = Math.floor(centerOfIntersectingVoxel.y / voxelSize) * voxelSize; + z = Math.floor(centerOfIntersectingVoxel.z / voxelSize) * voxelSize; + } + resultVoxel = { x: x, y: y, z: z, s: voxelSize }; + var highlightAt = { x: x, y: y, z: z, s: voxelSize }; + + + + // we only do the "add to the face we're pointing at" adjustment, if the operation is an add + // operation, and the target voxel size is equal to or smaller than the intersecting voxel. + var wantAddAdjust = (operation == "add" && (voxelSize <= intersection.voxel.s)); + + // now we also want to calculate the "edge square" for the face for this voxel + if (intersection.face == "MIN_X_FACE") { + + highlightAt.x = x - zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.x -= voxelSize; + } + + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + + } else if (intersection.face == "MAX_X_FACE") { + + highlightAt.x = x + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.x += resultVoxel.s; + } + + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + + } else if (intersection.face == "MIN_Y_FACE") { + + highlightAt.y = y - zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.y -= voxelSize; + } + + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + + } else if (intersection.face == "MAX_Y_FACE") { + + highlightAt.y = y + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.y += voxelSize; + } + + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; + + } else if (intersection.face == "MIN_Z_FACE") { + + highlightAt.z = z - zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.z -= voxelSize; + } + + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; + + } else if (intersection.face == "MAX_Z_FACE") { + + highlightAt.z = z + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.z += voxelSize; + } + + resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.bottomRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.topLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; + + } + return resultVoxel; +} + +var trackLastMouseX = 0; +var trackLastMouseY = 0; + +function showPreviewLines() { + + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + + var intersection = Voxels.findRayIntersection(pickRay); + + if (intersection.intersects) { + var resultVoxel = calculateVoxelFromIntersection(intersection, ""); + Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); + Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); + Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); + } else { + Overlays.editOverlay(linePreviewTop, { visible: false }); + Overlays.editOverlay(linePreviewBottom, { visible: false }); + Overlays.editOverlay(linePreviewLeft, { visible: false }); + Overlays.editOverlay(linePreviewRight, { visible: false }); + } +} + +function mouseMoveEvent(event) { + trackLastMouseX = event.x; + trackLastMouseY = event.y; + if (!voxelToolSelected) { + return; + } + showPreviewLines(); +} + + +// Array of possible trees, right now there is only one +var treeTypes = []; + +treeTypes.push({ + name: "Tall Green", + // Voxel Colors + wood: { r: 133, g: 81, b: 53 }, + leaves: { r: 22, g: 83, b: 31 }, + + // How tall the tree is + height: { min: 20, max: 60 }, + middleHeight: 0.3, + + // Chance of making a branch + branchChance: { min: 0.01, max: 0.1 }, + branchLength: { min: 30, max: 60 }, + branchThickness: { min: 2, max: 7}, + + // The width of the core, affects width and shape + coreWidth: { min: 1, max: 4 }, + + //TODO: Make this quadratic splines instead of linear + bottomThickness: { min: 2, max: 8 }, + middleThickness: { min: 1, max: 4 }, + topThickness: { min: 3, max: 6 }, + + //Modifies leaves at top + leafCapSizeOffset: 0 +}); + +// Applies noise to color +var colorNoiseRange = 0.2; + + +// Useful constants +var LEFT = 0; +var BACK = 1; +var RIGHT = 2; +var FRONT = 3; +var UP = 4; + +// Interpolates between min and max of treevar based on b +function interpolate(treeVar, b) { + return (treeVar.min + (treeVar.max - treeVar.min) * b); +} + +function makeBranch(x, y, z, step, length, dir, thickness, wood, leaves) { + var moveDir; + + var currentThickness; + //thickness attenuates to thickness - 3 + var finalThickness = thickness - 3; + if (finalThickness < 1) { + finalThickness = 1; + } + //Iterative branch generation + while (true) { + + //If we are at the end, place a ball of leaves + if (step == 0) { + makeSphere(x, y, z, 2 + finalThickness, leaves); + return; + } + //thickness attenuation + currentThickness = Math.round((finalThickness + (thickness - finalThickness) * (step/length))) - 1; + + + // If the branch is thick, grow a vertical slice + if (currentThickness > 0) { + for (var i = -currentThickness; i <= currentThickness; i++) { + var len = currentThickness - Math.abs(i); + switch (dir) { + case 0: //left + case 2: //right + growInDirection(x, y + i * voxelSize, z, len, len, BACK, wood, false, true); + growInDirection(x, y + i * voxelSize, z, len, len, FRONT, wood, false, false) + break; + case 1: //back + case 3: //front + growInDirection(x, y + i * voxelSize, z, len, len, LEFT, wood, false, true); + growInDirection(x, y + i * voxelSize, z, len, len, RIGHT, wood, false, false) + break; + } + } + } else { + //Otherwise place a single voxel + var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0; + Voxels.setVoxel(x, y, z, voxelSize, wood.r * colorNoise, wood.g * colorNoise, wood.b * colorNoise); + } + + // determines random change in direction for branch + var r = Math.floor(Math.random() * 9); + + + if (r >= 6){ + moveDir = dir; //in same direction + } else if (r >= 4) { + moveDir = UP; //up + } + else if (dir == LEFT){ + if (r >= 2){ + moveDir = FRONT; + } + else{ + moveDir = BACK; + } + } + else if (dir == BACK){ + if (r >= 2){ + moveDir = LEFT; + } + else{ + moveDir = RIGHT; + } + } + else if (dir == RIGHT){ + if (r >= 2){ + moveDir = BACK; + } + else{ + moveDir = FRONT; + } + } + else if (dir == FRONT){ + if (r >= 2){ + moveDir = RIGHT; + } + else{ + moveDir = LEFT; + } + } + + //Move the branch by moveDir + switch (moveDir) { + case 0: //left + x = x - voxelSize; + break; + case 1: //back + z = z - voxelSize; + break; + case 2: //right + x = x + voxelSize; + break; + case 3: //front + z = z + voxelSize; + break; + case 4: //up + y = y + voxelSize; + break; + } + + step--; + } +} + +// Places a sphere of voxels +function makeSphere(x, y, z, radius, color) { + if (radius <= 0) { + return; + } + var width = radius * 2 + 1; + var distance; + + for (var i = -radius; i <= radius; i++){ + for (var j = -radius; j <= radius; j++){ + for (var k = -radius; k <= radius; k++){ + distance = Math.sqrt(i * i + j * j + k * k); + if (distance <= radius){ + var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0; + Voxels.setVoxel(x + i * voxelSize, y + j * voxelSize, z + k * voxelSize, voxelSize, color.r * colorNoise, color.g * colorNoise, color.b * colorNoise); + } + } + } + } +} + +function growInDirection(x, y, z, step, length, dir, color, isSideBranching, addVoxel) { + + + if (addVoxel == true) { + var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0; + Voxels.setVoxel(x, y, z, voxelSize, color.r * colorNoise, color.g * colorNoise, color.b * colorNoise); + } + + // If this is a main vein, it will branch outward perpendicular to its motion + if (isSideBranching == true){ + var step2; + if (step >= length - 1){ + step2 = length; + } + else{ + step2 = step + 1; + } + growInDirection(x, y, z, step, length, BACK, color, false, false); + growInDirection(x, y, z, step, length, FRONT, color, false, false); + } + + if (step < 1) return; + + // Recursively move in the direction + if (dir == LEFT) { //left + growInDirection(x - voxelSize, y, z, step - 1, length, dir, color, isSideBranching, true); + } + else if (dir == BACK) { //back + growInDirection(x, y, z - voxelSize, step - 1, length, dir, color, isSideBranching, true); + } + else if (dir == RIGHT) { //right + growInDirection(x + voxelSize, y, z, step - 1, length, dir, color, isSideBranching, true); + } + else if (dir == FRONT) {//front + growInDirection(x, y, z + voxelSize, step - 1, length, dir, color, isSideBranching, true); + } + +} + +// Grows the thickness of the tree +function growHorizontalSlice(x, y, z, thickness, color, side) { + // The side variable determines which directions we should grow in + // it is an optimization that prevents us from visiting voxels multiple + // times for trees with a coreWidth > 1 + + // side: + // 8 == all directions + // 0 1 2 + // 3 -1 4 + // 5 6 7 + + Voxels.setVoxel(x, y, z, voxelSize, color.r, color.g, color.b); + + //We are done if there is no thickness + if (thickness == 0) { + return; + } + + + switch (side) { + case 0: + growInDirection(x, y, z, thickness, thickness, LEFT, color, true, false); + break; + case 1: + growInDirection(x, y, z, thickness, thickness, BACK, color, false, false); + break; + case 2: + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false); + break; + case 3: + growInDirection(x, y, z, thickness, thickness, LEFT, color, false, false); + break; + case 4: + growInDirection(x, y, z, thickness, thickness, BACK, color, false, false); + break; + case 5: + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false); + break; + case 6: + growInDirection(x, y, z, thickness, thickness, FRONT, color, false, false); + break; + case 7: + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false); + break; + case 8: + if (thickness > 1){ + growInDirection(x, y, z, thickness, thickness, LEFT, color, true, false); + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false) + } else if (thickness == 1){ + Voxels.setVoxel(x - voxelSize, y, z, voxelSize, color.r, color.g, color.b); + Voxels.setVoxel(x + voxelSize, y, z, voxelSize, color.r, color.g, color.b); + Voxels.setVoxel(x, y, z - voxelSize, voxelSize, color.r, color.g, color.b); + Voxels.setVoxel(x, y, z + voxelSize, voxelSize, color.r, color.g, color.b); + } + break; + } +} + +function computeSide(x, z, coreWidth) { + // side: + // 8 == all directions + // 0 1 2 + // 3 -1 4 + // 5 6 7 + + // if the core is only a single block, we can grow out in all directions + if (coreWidth == 1){ + return 8; + } + + // Back face + if (z == 0) { + if (x == 0) { + return 0; + } else if (x == coreWidth - 1) { + return 2; + } else { + return 1; + } + } + + // Front face + if (z == (coreWidth - 1)) { + if (x == 0) { + return 5; + } else if (x == (coreWidth - 1)) { + return 7; + } else { + return 6; + } + } + + // Left face + if (x == 0) { + return 3; + } + + // Right face + if (x == (coreWidth - 1)) { + return 4; + } + + //Interior + return -1; +} + + +function growTree(x, y, z, tree) { + + // The size of the tree, from 0-1 + var treeSize = Math.random(); + + // Get tree properties by interpolating with the treeSize + var height = interpolate(tree.height, treeSize); + var baseHeight = Math.ceil(tree.middleHeight * height); + var bottomThickness = interpolate(tree.bottomThickness, treeSize); + var middleThickness = interpolate(tree.middleThickness, treeSize); + var topThickness = interpolate(tree.topThickness, treeSize); + var coreWidth = Math.ceil(interpolate(tree.coreWidth, treeSize)); + + var thickness; + var side; + + //Loop upwards through each slice of the tree + for (var i = 0; i < height; i++){ + + //Branch properties are based on current height as well as the overall tree size + var branchChance = interpolate(tree.branchChance, i / height); + var branchLength = Math.ceil(interpolate(tree.branchLength, (i / height) * treeSize)); + var branchThickness = Math.round(interpolate(tree.branchThickness, (i / height) * treeSize)); + + // Get the "thickness" of the tree by doing linear interpolation between the middle thickness + // and the top and bottom thickness. + if (i <= baseHeight && baseHeight != 0){ + thickness = (i / (baseHeight) * (middleThickness - bottomThickness) + bottomThickness); + } else { + var denom = ((height - baseHeight)) * (topThickness - middleThickness) + middleThickness; + if (denom != 0) { + thickness = (i - baseHeight) / denom; + } else { + thickness = 0; + } + } + // The core of the tree is a vertical rectangular prism through the middle of the tree + + //Loop through the "core", which helps shape the trunk + var startX = x - Math.floor(coreWidth / 2) * voxelSize; + var startZ = z - Math.floor(coreWidth / 2) * voxelSize; + for (var j = 0; j < coreWidth; j++) { + for (var k = 0; k < coreWidth; k++) { + //determine which side of the tree we are on + side = computeSide(j, k, coreWidth); + //grow a horizontal slice of the tree + growHorizontalSlice(startX + j * voxelSize, y + i * voxelSize, startZ + k * voxelSize, Math.floor(thickness), tree.wood, side); + + // Branches + if (side != -1) { + var r = Math.random(); + if (r <= branchChance){ + var dir = Math.floor((Math.random() * 4)); + makeBranch(startX + j * voxelSize, y + i * voxelSize, startZ + k * voxelSize, branchLength, branchLength, dir, branchThickness, tree.wood, tree.leaves); + + } + } + } + } + } + + makeSphere(x, y + height * voxelSize, z, topThickness + coreWidth + tree.leafCapSizeOffset, tree.leaves); +} + +function mousePressEvent(event) { + var mouseX = event.x; + var mouseY = event.y; + + var clickedOnSomething = false; + // Check if we clicked an overlay + var clickedOverlay = Overlays.getOverlayAtPoint({x: mouseX, y: mouseY}); + + if (clickedOverlay == voxelTool) { + voxelToolSelected = !voxelToolSelected; + + if (voxelToolSelected == true) { + Overlays.editOverlay(voxelTool, { + color: activeUIColor + }); + } else { + Overlays.editOverlay(voxelTool, { + color: UIColor + }); + } + + clickedOnSomething = true; + } else if (scaleSelector.clicked(event.x, event.y)) { + clickedOnSomething = true; + voxelSize = scaleSelector.scale; + } + + // Return if we clicked on the UI or the voxel tool is disabled + if (clickedOnSomething || !voxelToolSelected) { + return; + } + + // Compute the picking ray for the click + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Voxels.findRayIntersection(pickRay); + var resultVoxel = calculateVoxelFromIntersection(intersection, "add"); + + // Currently not in use, could randomly select a tree + var treeIndex = Math.floor(Math.random() * treeTypes.length); + + // Grow the first tree type + growTree(resultVoxel.x, resultVoxel.y, resultVoxel.z, treeTypes[0]); + +} + +function update() { + +} + +function scriptEnding() { + Overlays.deleteOverlay(linePreviewTop); + Overlays.deleteOverlay(linePreviewBottom); + Overlays.deleteOverlay(linePreviewLeft); + Overlays.deleteOverlay(linePreviewRight); + scaleSelector.cleanup(); + Overlays.deleteOverlay(voxelTool); +} + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); + +Script.scriptEnding.connect(scriptEnding); + +Script.update.connect(update); + +Voxels.setPacketsPerSecond(10000); From 9aa152f43ff01938c5b8e87105426541c2357ba3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 10 Jun 2014 18:29:21 -0700 Subject: [PATCH 20/99] More TypeReader refactoring progress. --- libraries/metavoxels/src/Bitstream.cpp | 157 ++++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 60 +++++++++- 2 files changed, 213 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ca6cd5dd83..83ec86013c 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1950,11 +1950,22 @@ QVariant TypeStreamer::read(Bitstream& in) const { } void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - // nothing by default + if (value == reference) { + out << false; + } else { + out << true; + writeRawDelta(out, value, reference); + } } void TypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - value = reference; + bool changed; + in >> changed; + if (changed) { + readRawDelta(in, value, reference); + } else { + value = reference; + } } void TypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { @@ -2151,6 +2162,30 @@ void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) : + _baseStreamer(baseStreamer), + _bits(bits), + _mappings(mappings) { +} + +QVariant MappedEnumTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + int value = 0; + in.read(&value, _bits); + if (_baseStreamer) { + _baseStreamer->setEnumValue(object, value, _mappings); + } + return object; +} + +void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + int value = 0; + in.read(&value, _bits); + if (_baseStreamer) { + _baseStreamer->setEnumValue(object, value, _mappings); + } +} + GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : _streamer(streamer), _value(value) { @@ -2163,3 +2198,121 @@ bool GenericValue::operator==(const GenericValue& other) const { const TypeStreamer* GenericTypeStreamer::getStreamerToWrite(const QVariant& value) const { return value.value().getStreamer().data(); } + +MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : + _baseStreamer(baseStreamer), + _valueStreamer(valueStreamer) { +} + +QVariant MappedListTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant value = _valueStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->insert(object, value); + } + } + return object; +} + +void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + object = reference; + int size, referenceSize; + in >> size >> referenceSize; + if (_baseStreamer) { + if (size < referenceSize) { + _baseStreamer->prune(object, size); + } + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + QVariant value; + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); + _baseStreamer->setValue(object, i, value); + } else { + _baseStreamer->insert(object, _valueStreamer->read(in)); + } + } + } else { + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + QVariant value; + _valueStreamer->readDelta(in, value, QVariant()); + } else { + _valueStreamer->read(in); + } + } + } +} + +MappedSetTypeStreamer::MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : + MappedListTypeStreamer(baseStreamer, valueStreamer) { +} + +void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + object = reference; + int addedOrRemoved; + in >> addedOrRemoved; + for (int i = 0; i < addedOrRemoved; i++) { + QVariant value = _valueStreamer->read(in); + if (_baseStreamer && !_baseStreamer->remove(object, value)) { + _baseStreamer->insert(object, value); + } + } +} + +MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer) : + _baseStreamer(baseStreamer), + _keyStreamer(keyStreamer), + _valueStreamer(valueStreamer) { +} + +QVariant MappedMapTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value = _valueStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->insert(object, key, value); + } + } + return object; +} + +void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + object = reference; + int added; + in >> added; + for (int i = 0; i < added; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value = _valueStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->insert(object, key, value); + } + } + int modified; + in >> modified; + for (int i = 0; i < modified; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value; + if (_baseStreamer) { + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); + _baseStreamer->insert(object, key, value); + } else { + _valueStreamer->readDelta(in, value, QVariant()); + } + } + int removed; + in >> removed; + for (int i = 0; i < removed; i++) { + QVariant key = _keyStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->remove(object, key); + } + } +} + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index dadfe52911..cd7a0439aa 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -47,6 +47,7 @@ class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; +typedef QSharedPointer TypeStreamerPointer; typedef QVector PropertyReaderVector; typedef QVector PropertyWriterVector; @@ -904,8 +905,6 @@ Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); -typedef QSharedPointer TypeStreamerPointer; - /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: @@ -1014,6 +1013,22 @@ private: int _bits; }; +/// A streamer class for enums that maps to a local type. +class MappedEnumTypeStreamer : public TypeStreamer { +public: + + MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, int bits, const QHash& mappings); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +private: + + const TypeStreamer* _baseStreamer; + int _bits; + QHash _mappings; +}; + /// Contains a value along with a pointer to its streamer. class GenericValue { public: @@ -1089,6 +1104,21 @@ public: static_cast*>(object.data())->replace(index, value.value()); } }; +/// A streamer for lists that maps to a local type. +class MappedListTypeStreamer : public TypeStreamer { +public: + + MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +protected: + + const TypeStreamer* _baseStreamer; + TypeStreamerPointer _valueStreamer; +}; + /// A streamer for set types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: @@ -1101,6 +1131,15 @@ public: return static_cast*>(object.data())->remove(key.value()); } }; +/// A streamer for sets that maps to a local type. +class MappedSetTypeStreamer : public MappedListTypeStreamer { +public: + + MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer); + + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; +}; + /// A streamer for hash types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: @@ -1116,6 +1155,23 @@ public: return QVariant::fromValue(static_cast*>(object.constData())->value(key.value())); } }; +/// A streamer for maps that maps to a local type. +class MappedMapTypeStreamer : public TypeStreamer { +public: + + MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +private: + + const TypeStreamer* _baseStreamer; + TypeStreamerPointer _keyStreamer; + TypeStreamerPointer _valueStreamer; +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); From a48f38b1d263a04f0fba5fce7a3502175e2078b9 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 21:32:23 -0400 Subject: [PATCH 21/99] Revert "Job #19766 BUG: Stop or reload all scripts crashes interface fix, part 2." This reverts commit b4e984086505814cbc426226777b74a113da8362. --- interface/src/Application.cpp | 5 +++-- libraries/particles/src/Particle.cpp | 6 +++--- libraries/script-engine/src/ScriptEngine.cpp | 12 +++++++----- libraries/script-engine/src/ScriptEngine.h | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3ec5588c61..9fb821bff4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3492,8 +3492,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - scriptEngine = new ScriptEngine(scriptName, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptName, scriptEngine); + QUrl scriptUrl(scriptName); + scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); + _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 90e59b9422..59265c00dc 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -873,7 +873,7 @@ void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObje void Particle::executeUpdateScripts() { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script, QString("")); + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitUpdate(); @@ -884,7 +884,7 @@ void Particle::executeUpdateScripts() { void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script, QString("")); + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); ParticleScriptObject otherParticleScriptable(other); @@ -896,7 +896,7 @@ void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetrati void Particle::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script, QString("")); + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 025f316d29..a4aae61248 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -93,7 +93,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam { } -ScriptEngine::ScriptEngine(const QString& fileNameString, +ScriptEngine::ScriptEngine(const QUrl& scriptURL, AbstractControllerScriptingInterface* controllerScriptingInterface) : _scriptContents(), _isFinished(false), @@ -110,19 +110,21 @@ ScriptEngine::ScriptEngine(const QString& fileNameString, _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _scriptName(), - _fileNameString(fileNameString), + _fileNameString(), _quatLibrary(), _vec3Library(), _uuidLibrary(), _animationCache(this) { - QUrl url(fileNameString); - QString scriptUrlString = url.toString(); + QString scriptURLString = scriptURL.toString(); + _fileNameString = scriptURLString; + + QUrl url(scriptURL); // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { - url = QUrl::fromLocalFile(scriptUrlString); + url = QUrl::fromLocalFile(scriptURLString); } // ok, let's see if it's valid... and if so, load it diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f88017515e..bf2ac40568 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -40,7 +40,7 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10 class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(const QString& fileNameString, + ScriptEngine(const QUrl& scriptURL, AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ScriptEngine(const QString& scriptContents = NO_SCRIPT, From 46f2ab73bccf6fa8669856534f0c20f96ee8c416 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 22:29:58 -0400 Subject: [PATCH 22/99] Job #19766 BUG: Stop or reload all scripts crashes interface fixed, part 3 Move the conversion of scriptName to QUrl to the beginning of Application::loadScript. Use the scriptURLString to query the _scriptEngineHash map. --- CMakeLists.txt | 4 + assignment-client/src/AssignmentClient.cpp | 123 +++++++++++++++------ assignment-client/src/AssignmentClient.h | 48 ++++++++ interface/src/Application.cpp | 9 +- interface/src/Audio.cpp | 29 ++++- 5 files changed, 175 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1e4224cf..298b9a791e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,10 @@ if (NOT QT_CMAKE_PREFIX_PATH) set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) endif () +if (NOT ZLIB_ROOT) + set(ZLIB_ROOT $ENV{ZLIB_ROOT}) +endif () + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH}) # set our Base SDK to 10.8 diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 009bd42e88..a2faaab651 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -33,6 +33,64 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment; int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); +template +class Parse : public Parser { +public: + Parse(const QString& option, bool required, T* value) : Parser(option, required), _value(value) {} + bool parse(const QStringList& argumentList); +private: + T* _value; +}; + +template<> +bool Parse::parse(const QStringList& argumentList) { + int argumentIndex = findToken(argumentList); + if (argumentIndex != -1) { + argumentIndex++; + if (argumentList.size() > argumentIndex) { + bool ok; + int value = argumentList[argumentIndex].toInt(&ok); + if (ok) { + *_value = static_cast(value); + } + validateValue(ok); + } else { + valueNotSpecified(); + } + } + return isValid(); +} + +template<> +bool Parse::parse(const QStringList& argumentList) { + int argumentIndex = findToken(argumentList); + if (argumentIndex != -1) { + argumentIndex++; + if (argumentList.size() > argumentIndex) { + *_value = QString(argumentList[argumentIndex]); + validateValue(!_value->isNull()); + } else { + valueNotSpecified(); + } + } + return isValid(); +} + +template<> +bool Parse::parse(const QStringList& argumentList) { + int argumentIndex = findToken(argumentList); + if (argumentIndex != -1) { + argumentIndex++; + if (argumentList.size() > argumentIndex) { + *_value = argumentList[argumentIndex]; + validateValue(true); + } else { + valueNotSpecified(); + } + } + return isValid(); +} + AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) @@ -43,40 +101,42 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : QSettings::setDefaultFormat(QSettings::IniFormat); QStringList argumentList = arguments(); - + + // Define parser tokens + const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "-t"; + const QString ASSIGNMENT_POOL_OPTION = "--pool"; + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; + const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; + + // Define parser results + Assignment::Type requestAssignmentType = Assignment::AllTypes; + QString assignmentPool; + QUuid walletUUID; + + // Define parsers + _parsers.insert(ASSIGNMENT_TYPE_OVERRIDE_OPTION, new Parse(ASSIGNMENT_TYPE_OVERRIDE_OPTION, false, &requestAssignmentType)); + _parsers.insert(ASSIGNMENT_POOL_OPTION, new Parse(ASSIGNMENT_POOL_OPTION, false, &assignmentPool)); + _parsers.insert(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, new Parse(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, false, &walletUUID)); + _parsers.insert(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, new Parse(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, false, &_assignmentServerHostname)); + // register meta type is required for queued invoke method on Assignment subclasses // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t"; - int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION); - - Assignment::Type requestAssignmentType = Assignment::AllTypes; - - if (argumentIndex != -1) { - requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt(); + foreach (Parser* option, _parsers) { + if (option) { + option->parse(argumentList); + } } - - const QString ASSIGNMENT_POOL_OPTION = "--pool"; - - argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION); - - QString assignmentPool; - - if (argumentIndex != -1) { - assignmentPool = argumentList[argumentIndex + 1]; - } - + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); // check if we were passed a wallet UUID on the command line // this would represent where the user running AC wants funds sent to - - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; - if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { - QUuid walletUUID = QString(argumentList[argumentIndex + 1]); + if (_parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->exists() && + _parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->isValid()) { qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } @@ -84,14 +144,9 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); - // check for an overriden assignment server hostname - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; - - argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); - - if (argumentIndex != -1) { - _assignmentServerHostname = argumentList[argumentIndex + 1]; - + // check for an overriden assignment server hostname + if (_parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->exists() && + _parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->isValid()) { // set the custom assignment socket on our NodeList HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT); @@ -113,6 +168,12 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : this, &AssignmentClient::handleAuthenticationRequest); } +AssignmentClient::~AssignmentClient() { + foreach (Parser* option, _parsers) { + delete option; + } +} + void AssignmentClient::sendAssignmentRequest() { if (!_currentAssignment) { NodeList::getInstance()->sendAssignment(_requestAssignment); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 9834402dbf..8951d44135 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -16,11 +16,57 @@ #include "ThreadedAssignment.h" +class Parser { +public: + Parser(const QString& option, bool required) : _option(option), _required(required), _exists(false), _isValid(false) {} + virtual bool parse(const QStringList& argumentList) { return false; } + bool exists() { return _exists; } + bool isValid() { return _isValid; } + +protected: + int findToken(const QStringList& argumentList) { + int argumentIndex = argumentList.indexOf(_option); + validateOption(argumentIndex); + return argumentIndex; + } + + void validateOption(int index) { + _exists = true; + if (index == -1) { + _exists = false; + if (_required) { + qDebug() << "Command line option " << _option << " is missing"; + } + } + } + + void valueNotSpecified() { + _isValid = false; + qDebug() << "Command line option " << _option << " value is missing"; + } + + void validateValue(bool ok) { + _isValid = ok; + if (!ok) { + qDebug() << "Command line option " << _option << " value is invalid"; + } + } + +private: + QString _option; + bool _required; + bool _exists; + bool _isValid; +}; + class AssignmentClient : public QCoreApplication { Q_OBJECT public: AssignmentClient(int &argc, char **argv); + ~AssignmentClient(); + static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } + private slots: void sendAssignmentRequest(); void readPendingDatagrams(); @@ -31,6 +77,8 @@ private: Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; QString _assignmentServerHostname; + + QMap _parsers; }; #endif // hifi_AssignmentClient_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fb821bff4..25a5ba8007 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3483,8 +3483,10 @@ void Application::saveScripts() { } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { - if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ - return _scriptEnginesHash[scriptName]; + QUrl scriptUrl(scriptName); + const QString& scriptURLString = scriptUrl.toString(); + if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){ + return _scriptEnginesHash[scriptURLString]; } ScriptEngine* scriptEngine; @@ -3492,9 +3494,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); + _scriptEnginesHash.insert(scriptURLString, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ebc228a13d..1ecfad9007 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -219,8 +219,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { pPropertyStore->Release(); pPropertyStore = NULL; //QAudio devices seems to only take the 31 first characters of the Friendly Device Name. - const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; - deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); + //const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; + // deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); + deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); qDebug() << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName; PropVariantClear(&pv); } @@ -1077,13 +1078,35 @@ void Audio::renderToolBox(int x, int y, bool boxed) { } _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); + + static quint64 last = 0; // Hold the last time sample + if (!_muted) { + last = 0; + glColor3f(1,1,1); glBindTexture(GL_TEXTURE_2D, _micTextureId); } else { + quint64 usecTimestampNow(); + static const float CYCLE_DURATION = 3.0f; // Seconds + quint64 now = usecTimestampNow(); + float delta = (float)(now - last) / 1000000.0f; + float from = 1.0f; // Linear fade down (upper bound) + float to = 0.3f; // Linear fade down (lower bound) + + if (delta > CYCLE_DURATION) { + last = now; + delta -= (int)delta; + } else if (delta > (CYCLE_DURATION * 0.5f)) { + // Linear fade up + from = 0.3f; // lower bound + to = 1.0f; // upper bound + } + // Compute a linear ramp to fade the color from full to partial saturation + float linearRamp = (from - to) * delta / CYCLE_DURATION + to; + glColor3f(linearRamp, linearRamp, linearRamp); glBindTexture(GL_TEXTURE_2D, _muteTextureId); } - glColor3f(1,1,1); glBegin(GL_QUADS); glTexCoord2f(1, 1); From 1e1cc69287d8cdaaf304bbfc05da56ae9564016f Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 22:37:34 -0400 Subject: [PATCH 23/99] Revert "Job #19766 BUG: Stop or reload all scripts crashes interface fixed, part 3" This reverts commit 46f2ab73bccf6fa8669856534f0c20f96ee8c416. --- CMakeLists.txt | 4 - assignment-client/src/AssignmentClient.cpp | 123 ++++++--------------- assignment-client/src/AssignmentClient.h | 48 -------- interface/src/Application.cpp | 9 +- interface/src/Audio.cpp | 29 +---- 5 files changed, 38 insertions(+), 175 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 298b9a791e..cb1e4224cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,10 +20,6 @@ if (NOT QT_CMAKE_PREFIX_PATH) set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) endif () -if (NOT ZLIB_ROOT) - set(ZLIB_ROOT $ENV{ZLIB_ROOT}) -endif () - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH}) # set our Base SDK to 10.8 diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index a2faaab651..009bd42e88 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -33,64 +33,6 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment; int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); -template -class Parse : public Parser { -public: - Parse(const QString& option, bool required, T* value) : Parser(option, required), _value(value) {} - bool parse(const QStringList& argumentList); -private: - T* _value; -}; - -template<> -bool Parse::parse(const QStringList& argumentList) { - int argumentIndex = findToken(argumentList); - if (argumentIndex != -1) { - argumentIndex++; - if (argumentList.size() > argumentIndex) { - bool ok; - int value = argumentList[argumentIndex].toInt(&ok); - if (ok) { - *_value = static_cast(value); - } - validateValue(ok); - } else { - valueNotSpecified(); - } - } - return isValid(); -} - -template<> -bool Parse::parse(const QStringList& argumentList) { - int argumentIndex = findToken(argumentList); - if (argumentIndex != -1) { - argumentIndex++; - if (argumentList.size() > argumentIndex) { - *_value = QString(argumentList[argumentIndex]); - validateValue(!_value->isNull()); - } else { - valueNotSpecified(); - } - } - return isValid(); -} - -template<> -bool Parse::parse(const QStringList& argumentList) { - int argumentIndex = findToken(argumentList); - if (argumentIndex != -1) { - argumentIndex++; - if (argumentList.size() > argumentIndex) { - *_value = argumentList[argumentIndex]; - validateValue(true); - } else { - valueNotSpecified(); - } - } - return isValid(); -} - AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) @@ -101,42 +43,40 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : QSettings::setDefaultFormat(QSettings::IniFormat); QStringList argumentList = arguments(); - - // Define parser tokens - const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "-t"; - const QString ASSIGNMENT_POOL_OPTION = "--pool"; - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; - - // Define parser results - Assignment::Type requestAssignmentType = Assignment::AllTypes; - QString assignmentPool; - QUuid walletUUID; - - // Define parsers - _parsers.insert(ASSIGNMENT_TYPE_OVERRIDE_OPTION, new Parse(ASSIGNMENT_TYPE_OVERRIDE_OPTION, false, &requestAssignmentType)); - _parsers.insert(ASSIGNMENT_POOL_OPTION, new Parse(ASSIGNMENT_POOL_OPTION, false, &assignmentPool)); - _parsers.insert(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, new Parse(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, false, &walletUUID)); - _parsers.insert(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, new Parse(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, false, &_assignmentServerHostname)); - + // register meta type is required for queued invoke method on Assignment subclasses // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - foreach (Parser* option, _parsers) { - if (option) { - option->parse(argumentList); - } + const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t"; + int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION); + + Assignment::Type requestAssignmentType = Assignment::AllTypes; + + if (argumentIndex != -1) { + requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt(); } - + + const QString ASSIGNMENT_POOL_OPTION = "--pool"; + + argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION); + + QString assignmentPool; + + if (argumentIndex != -1) { + assignmentPool = argumentList[argumentIndex + 1]; + } + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); // check if we were passed a wallet UUID on the command line // this would represent where the user running AC wants funds sent to - if (_parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->exists() && - _parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->isValid()) { + + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; + if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { + QUuid walletUUID = QString(argumentList[argumentIndex + 1]); qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } @@ -144,9 +84,14 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); - // check for an overriden assignment server hostname - if (_parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->exists() && - _parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->isValid()) { + // check for an overriden assignment server hostname + const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; + + argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); + + if (argumentIndex != -1) { + _assignmentServerHostname = argumentList[argumentIndex + 1]; + // set the custom assignment socket on our NodeList HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT); @@ -168,12 +113,6 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : this, &AssignmentClient::handleAuthenticationRequest); } -AssignmentClient::~AssignmentClient() { - foreach (Parser* option, _parsers) { - delete option; - } -} - void AssignmentClient::sendAssignmentRequest() { if (!_currentAssignment) { NodeList::getInstance()->sendAssignment(_requestAssignment); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 8951d44135..9834402dbf 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -16,57 +16,11 @@ #include "ThreadedAssignment.h" -class Parser { -public: - Parser(const QString& option, bool required) : _option(option), _required(required), _exists(false), _isValid(false) {} - virtual bool parse(const QStringList& argumentList) { return false; } - bool exists() { return _exists; } - bool isValid() { return _isValid; } - -protected: - int findToken(const QStringList& argumentList) { - int argumentIndex = argumentList.indexOf(_option); - validateOption(argumentIndex); - return argumentIndex; - } - - void validateOption(int index) { - _exists = true; - if (index == -1) { - _exists = false; - if (_required) { - qDebug() << "Command line option " << _option << " is missing"; - } - } - } - - void valueNotSpecified() { - _isValid = false; - qDebug() << "Command line option " << _option << " value is missing"; - } - - void validateValue(bool ok) { - _isValid = ok; - if (!ok) { - qDebug() << "Command line option " << _option << " value is invalid"; - } - } - -private: - QString _option; - bool _required; - bool _exists; - bool _isValid; -}; - class AssignmentClient : public QCoreApplication { Q_OBJECT public: AssignmentClient(int &argc, char **argv); - ~AssignmentClient(); - static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } - private slots: void sendAssignmentRequest(); void readPendingDatagrams(); @@ -77,8 +31,6 @@ private: Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; QString _assignmentServerHostname; - - QMap _parsers; }; #endif // hifi_AssignmentClient_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 25a5ba8007..9fb821bff4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3483,10 +3483,8 @@ void Application::saveScripts() { } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { - QUrl scriptUrl(scriptName); - const QString& scriptURLString = scriptUrl.toString(); - if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){ - return _scriptEnginesHash[scriptURLString]; + if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ + return _scriptEnginesHash[scriptName]; } ScriptEngine* scriptEngine; @@ -3494,8 +3492,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... + QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptURLString, scriptEngine); + _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 1ecfad9007..ebc228a13d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -219,9 +219,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { pPropertyStore->Release(); pPropertyStore = NULL; //QAudio devices seems to only take the 31 first characters of the Friendly Device Name. - //const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; - // deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); - deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); + const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; + deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); qDebug() << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName; PropVariantClear(&pv); } @@ -1078,35 +1077,13 @@ void Audio::renderToolBox(int x, int y, bool boxed) { } _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); - - static quint64 last = 0; // Hold the last time sample - if (!_muted) { - last = 0; - glColor3f(1,1,1); glBindTexture(GL_TEXTURE_2D, _micTextureId); } else { - quint64 usecTimestampNow(); - static const float CYCLE_DURATION = 3.0f; // Seconds - quint64 now = usecTimestampNow(); - float delta = (float)(now - last) / 1000000.0f; - float from = 1.0f; // Linear fade down (upper bound) - float to = 0.3f; // Linear fade down (lower bound) - - if (delta > CYCLE_DURATION) { - last = now; - delta -= (int)delta; - } else if (delta > (CYCLE_DURATION * 0.5f)) { - // Linear fade up - from = 0.3f; // lower bound - to = 1.0f; // upper bound - } - // Compute a linear ramp to fade the color from full to partial saturation - float linearRamp = (from - to) * delta / CYCLE_DURATION + to; - glColor3f(linearRamp, linearRamp, linearRamp); glBindTexture(GL_TEXTURE_2D, _muteTextureId); } + glColor3f(1,1,1); glBegin(GL_QUADS); glTexCoord2f(1, 1); From 679846267e32a0929045dd86cd914f8c4c80ff91 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 22:42:01 -0400 Subject: [PATCH 24/99] Job #19766 BUG: Stop or reload all scripts crashes interface fixed, part 3 Same comments as before, but without the local files changes mistakenly checked in. --- interface/src/Application.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fb821bff4..25a5ba8007 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3483,8 +3483,10 @@ void Application::saveScripts() { } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { - if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ - return _scriptEnginesHash[scriptName]; + QUrl scriptUrl(scriptName); + const QString& scriptURLString = scriptUrl.toString(); + if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){ + return _scriptEnginesHash[scriptURLString]; } ScriptEngine* scriptEngine; @@ -3492,9 +3494,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); + _scriptEnginesHash.insert(scriptURLString, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; From 41b9c3b7049cc46732cafa9bf833c5c1537b0fd5 Mon Sep 17 00:00:00 2001 From: Dev5 Date: Wed, 11 Jun 2014 09:06:00 -0700 Subject: [PATCH 25/99] Removed empty update function --- examples/growTrees.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/growTrees.js b/examples/growTrees.js index d853804e77..142c72300a 100644 --- a/examples/growTrees.js +++ b/examples/growTrees.js @@ -814,9 +814,6 @@ function mousePressEvent(event) { } -function update() { - -} function scriptEnding() { Overlays.deleteOverlay(linePreviewTop); @@ -832,6 +829,4 @@ Controller.mouseMoveEvent.connect(mouseMoveEvent); Script.scriptEnding.connect(scriptEnding); -Script.update.connect(update); - Voxels.setPacketsPerSecond(10000); From fc07ceb412d1975d3b3fc07374073e3eb55bc1c9 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 10:54:44 -0700 Subject: [PATCH 26/99] added killNode() slot for _packetCounts garbage collection --- interface/src/Application.cpp | 1 + libraries/networking/src/ReceivedPacketProcessor.cpp | 4 ++++ libraries/networking/src/ReceivedPacketProcessor.h | 3 +++ 3 files changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eab15ed678..2b3256141e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,6 +244,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); + connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_octreeProcessor, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&))); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 46f1515016..d556e8a059 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -49,3 +49,7 @@ bool ReceivedPacketProcessor::process() { } return isStillRunning(); // keep running till they terminate us } + +void ReceivedPacketProcessor::killNode(const SharedNodePointer& node) { + _nodePacketCounts.remove(node->getUUID()); +} diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 096005ffe7..80fe75aaa7 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -41,6 +41,9 @@ public: /// How many received packets waiting are to be processed int packetsToProcessCount() const { return _packets.size(); } +public slots: + void killNode(const SharedNodePointer& node); + protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. /// \param SharedNodePointer& sendingNode the node that sent this packet From 760627003f353a1552e8e94b050698b2c233bd5a Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 11:11:04 -0700 Subject: [PATCH 27/99] killNode() to nodeKilled() --- libraries/networking/src/ReceivedPacketProcessor.cpp | 4 ++-- libraries/networking/src/ReceivedPacketProcessor.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index d556e8a059..e919017128 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -50,6 +50,6 @@ bool ReceivedPacketProcessor::process() { return isStillRunning(); // keep running till they terminate us } -void ReceivedPacketProcessor::killNode(const SharedNodePointer& node) { - _nodePacketCounts.remove(node->getUUID()); +void ReceivedPacketProcessor::nodeKilled(const SharedNodePointer& node) { + int numRemoved = _nodePacketCounts.remove(node->getUUID()); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 80fe75aaa7..23303cbb55 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -42,7 +42,7 @@ public: int packetsToProcessCount() const { return _packets.size(); } public slots: - void killNode(const SharedNodePointer& node); + void nodeKilled(const SharedNodePointer& node); protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. From 6b48554fdd39d685bcffb56bbcaed94ffe4b3275 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 11:26:21 -0700 Subject: [PATCH 28/99] Added explanatory comment. --- libraries/metavoxels/src/ScriptCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 9648a047cb..ffd5200a2e 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -90,6 +90,7 @@ bool operator==(const QScriptValue& first, const QScriptValue& second) { return true; } else { + // if none of the above tests apply, first must be invalid return !second.isValid(); } } From decd6109fbda6682d97ae23432bfe9b2ac5c4cd8 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 11:57:54 -0700 Subject: [PATCH 29/99] made nodeKilled signatures match --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- libraries/networking/src/ReceivedPacketProcessor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index e919017128..59165b45b6 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -50,6 +50,6 @@ bool ReceivedPacketProcessor::process() { return isStillRunning(); // keep running till they terminate us } -void ReceivedPacketProcessor::nodeKilled(const SharedNodePointer& node) { +void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { int numRemoved = _nodePacketCounts.remove(node->getUUID()); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 23303cbb55..94ad2d9c41 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -42,7 +42,7 @@ public: int packetsToProcessCount() const { return _packets.size(); } public slots: - void nodeKilled(const SharedNodePointer& node); + void nodeKilled(SharedNodePointer node); protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. From 2080cc3dee753d072b005bbac3e868ab8bd0716a Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 11:59:15 -0700 Subject: [PATCH 30/99] removed debug variable --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 59165b45b6..32fc4e26f3 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -51,5 +51,5 @@ bool ReceivedPacketProcessor::process() { } void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { - int numRemoved = _nodePacketCounts.remove(node->getUUID()); + _nodePacketCounts.remove(node->getUUID()); } From a8c97800048043e4908743601ae95b3c92fe80d9 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 12:13:27 -0700 Subject: [PATCH 31/99] updated connect() to qt5 format --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b3256141e..a5cb6a0c12 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,7 +244,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); - connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_octreeProcessor, SLOT(nodeKilled(SharedNodePointer))); + connect(NodeList::getInstance(), &NodeList::nodeKilled, &_octreeProcessor, &ReceivedPacketProcessor::nodeKilled); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&))); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); From a529bc7e28579c5663c03f70c6fdf1020d69e2b9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 13:40:12 -0700 Subject: [PATCH 32/99] Refactoring. --- libraries/metavoxels/src/Bitstream.cpp | 463 +++++++------------------ libraries/metavoxels/src/Bitstream.h | 117 ++----- 2 files changed, 157 insertions(+), 423 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 83ec86013c..ce46429393 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -301,9 +301,9 @@ void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) } void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { - TypeReader typeReader; - _typeStreamerStreamer >> typeReader; - typeReader.readRawDelta(*this, value, reference); + TypeStreamerPointer typeStreamer; + _typeStreamerStreamer >> typeStreamer; + typeStreamer->readRawDelta(*this, value, reference); } void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { @@ -767,12 +767,12 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } Bitstream& Bitstream::operator>>(QVariant& value) { - TypeReader reader; - _typeStreamerStreamer >> reader; - if (reader.getTypeName().isEmpty()) { + TypeStreamerPointer streamer; + _typeStreamerStreamer >> streamer; + if (!streamer) { value = QVariant(); } else { - value = reader.read(*this); + value = streamer->read(*this); } return *this; } @@ -853,14 +853,14 @@ Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { } Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { - TypeReader typeReader; - _typeStreamerStreamer >> typeReader; - streamer = typeReader.getStreamer(); + TypeStreamerPointer typeStreamer; + _typeStreamerStreamer >> typeStreamer; + streamer = typeStreamer.data(); return *this; } -Bitstream& Bitstream::operator>>(TypeReader& reader) { - _typeStreamerStreamer >> reader; +Bitstream& Bitstream::operator>>(TypeStreamerPointer& streamer) { + _typeStreamerStreamer >> streamer; return *this; } @@ -1122,8 +1122,8 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { *this >> storedPropertyCount; PropertyReaderVector properties(storedPropertyCount); for (int i = 0; i < storedPropertyCount; i++) { - TypeReader typeReader; - *this >> typeReader; + TypeStreamerPointer typeStreamer; + *this >> typeStreamer; QMetaProperty property = QMetaProperty(); if (_metadataType == FULL_METADATA) { QByteArray propertyName; @@ -1132,7 +1132,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { property = metaObject->property(metaObject->indexOfProperty(propertyName)); } } - properties[i] = PropertyReader(typeReader, property); + properties[i] = PropertyReader(typeStreamer, property); } // for hash metadata, check the names/types of the properties as well as the name hash against our own class if (_metadataType == HASH_METADATA) { @@ -1143,7 +1143,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { if (propertyWriters.size() == properties.size()) { for (int i = 0; i < propertyWriters.size(); i++) { const PropertyWriter& propertyWriter = propertyWriters.at(i); - if (!properties.at(i).getReader().matchesExactly(propertyWriter.getStreamer())) { + if (properties.at(i).getStreamer() != propertyWriter.getStreamer()) { matches = false; break; } @@ -1176,13 +1176,13 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { if (_metadataType == NO_METADATA) { return *this; } - TypeReader::Type type = streamer->getReaderType(); - *this << (int)type; - switch (type) { - case TypeReader::SIMPLE_TYPE: + TypeStreamer::Category category = streamer->getCategory(); + *this << (int)category; + switch (category) { + case TypeStreamer::SIMPLE_CATEGORY: return *this; - case TypeReader::ENUM_TYPE: { + case TypeStreamer::ENUM_CATEGORY: { QMetaEnum metaEnum = streamer->getMetaEnum(); if (_metadataType == FULL_METADATA) { *this << metaEnum.keyCount(); @@ -1203,11 +1203,11 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { } return *this; } - case TypeReader::LIST_TYPE: - case TypeReader::SET_TYPE: + case TypeStreamer::LIST_CATEGORY: + case TypeStreamer::SET_CATEGORY: return *this << streamer->getValueStreamer(); - case TypeReader::MAP_TYPE: + case TypeStreamer::MAP_CATEGORY: return *this << streamer->getKeyStreamer() << streamer->getValueStreamer(); default: @@ -1235,46 +1235,50 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { return *this; } -Bitstream& Bitstream::operator>(TypeReader& reader) { +Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { QByteArray typeName; *this >> typeName; if (typeName.isEmpty()) { - reader = TypeReader(); + streamer = TypeStreamerPointer(); return *this; } - const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName); - if (!streamer) { - streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); - if (!streamer) { - streamer = getEnumStreamersByName().value(typeName); + const TypeStreamer* baseStreamer = _typeStreamerSubstitutions.value(typeName); + if (!baseStreamer) { + baseStreamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + if (!baseStreamer) { + baseStreamer = getEnumStreamersByName().value(typeName); } } + // start out with the base, if any + if (baseStreamer) { + streamer = baseStreamer->getSelf(); + } else { + streamer = TypeStreamerPointer(); + } if (_metadataType == NO_METADATA) { - if (!streamer) { + if (!baseStreamer) { qWarning() << "Unknown type name:" << typeName; } - reader = TypeReader(typeName, streamer); return *this; } - int type; - *this >> type; - if (type == TypeReader::SIMPLE_TYPE) { + int category; + *this >> category; + if (category == TypeStreamer::SIMPLE_CATEGORY) { if (!streamer) { qWarning() << "Unknown type name:" << typeName; } - reader = TypeReader(typeName, streamer); return *this; } if (_genericsMode == ALL_GENERICS) { - streamer = NULL; + baseStreamer = NULL; } - switch (type) { - case TypeReader::ENUM_TYPE: { + switch (category) { + case TypeStreamer::ENUM_CATEGORY: { if (_metadataType == FULL_METADATA) { int keyCount; *this >> keyCount; - QMetaEnum metaEnum = (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) ? - streamer->getMetaEnum() : QMetaEnum(); + QMetaEnum metaEnum = (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) ? + baseStreamer->getMetaEnum() : QMetaEnum(); QHash mappings; bool matches = (keyCount == metaEnum.keyCount()); int highestValue = 0; @@ -1289,17 +1293,16 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { } matches &= (value == localValue); } - if (matches) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, getBitsForHighestValue(highestValue), mappings); + if (!matches) { + streamer = TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, + getBitsForHighestValue(highestValue), mappings)); } } else { int bits; *this >> bits; QCryptographicHash hash(QCryptographicHash::Md5); - if (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) { - QMetaEnum metaEnum = streamer->getMetaEnum(); + if (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { + QMetaEnum metaEnum = baseStreamer->getMetaEnum(); for (int i = 0; i < metaEnum.keyCount(); i++) { hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); qint32 value = metaEnum.value(i); @@ -1309,37 +1312,30 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (localHashResult == remoteHashResult) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, bits, QHash()); + if (localHashResult != remoteHashResult) { + streamer = TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, bits, QHash())); } } return *this; } - case TypeReader::LIST_TYPE: - case TypeReader::SET_TYPE: { - TypeReader valueReader; - *this >> valueReader; - if (streamer && streamer->getReaderType() == type && - valueReader.matchesExactly(streamer->getValueStreamer())) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, (TypeReader::Type)type, - TypeReaderPointer(new TypeReader(valueReader))); + case TypeStreamer::LIST_CATEGORY: + case TypeStreamer::SET_CATEGORY: { + TypeStreamerPointer valueStreamer; + *this >> valueStreamer; + if (!(baseStreamer && baseStreamer->getCategory() == category && + valueStreamer == baseStreamer->getValueStreamer())) { + streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? + new MappedListTypeStreamer(baseStreamer, valueStreamer) : + new MappedSetTypeStreamer(baseStreamer, valueStreamer)); } return *this; } - case TypeReader::MAP_TYPE: { - TypeReader keyReader, valueReader; - *this >> keyReader >> valueReader; - if (streamer && streamer->getReaderType() == TypeReader::MAP_TYPE && - keyReader.matchesExactly(streamer->getKeyStreamer()) && - valueReader.matchesExactly(streamer->getValueStreamer())) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, TypeReaderPointer(new TypeReader(keyReader)), - TypeReaderPointer(new TypeReader(valueReader))); + case TypeStreamer::MAP_CATEGORY: { + TypeStreamerPointer keyStreamer, valueStreamer; + *this >> keyStreamer >> valueStreamer; + if (!(baseStreamer && baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && + keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer())) { + streamer = TypeStreamerPointer(new MappedMapTypeStreamer(baseStreamer, keyStreamer, valueStreamer)); } return *this; } @@ -1347,37 +1343,36 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { // streamable type int fieldCount; *this >> fieldCount; - QVector fields(fieldCount); + QVector fields(fieldCount); for (int i = 0; i < fieldCount; i++) { - TypeReader typeReader; - *this >> typeReader; + TypeStreamerPointer typeStreamer; + *this >> typeStreamer; int index = -1; if (_metadataType == FULL_METADATA) { QByteArray fieldName; *this >> fieldName; - if (streamer) { - index = streamer->getFieldIndex(fieldName); + if (baseStreamer) { + index = baseStreamer->getFieldIndex(fieldName); } } - fields[i] = FieldReader(typeReader, index); + fields[i] = StreamerIndexPair(typeStreamer, index); } // for hash metadata, check the names/types of the fields as well as the name hash against our own class if (_metadataType == HASH_METADATA) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; - if (streamer) { - const QVector& localFields = streamer->getMetaFields(); + if (baseStreamer) { + const QVector& localFields = baseStreamer->getMetaFields(); if (fieldCount != localFields.size()) { matches = false; } else { if (fieldCount == 0) { - reader = TypeReader(typeName, streamer); return *this; } for (int i = 0; i < fieldCount; i++) { const MetaField& localField = localFields.at(i); - if (!fields.at(i).getReader().matchesExactly(localField.getStreamer())) { + if (fields.at(i).first != localField.getStreamer()) { matches = false; break; } @@ -1388,29 +1383,27 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (streamer && matches && localHashResult == remoteHashResult) { + if (baseStreamer && matches && localHashResult == remoteHashResult) { // since everything is the same, we can use the default streamer - reader = TypeReader(typeName, streamer); return *this; } - } else if (streamer) { + } else if (baseStreamer) { // if all fields are the same type and in the right order, we can use the (more efficient) default streamer - const QVector& localFields = streamer->getMetaFields(); + const QVector& localFields = baseStreamer->getMetaFields(); if (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, fields); + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } for (int i = 0; i < fieldCount; i++) { - const FieldReader& fieldReader = fields.at(i); - if (!fieldReader.getReader().matchesExactly(localFields.at(i).getStreamer()) || fieldReader.getIndex() != i) { - reader = TypeReader(typeName, streamer, fields); + const StreamerIndexPair& field = fields.at(i); + if (field.first != localFields.at(i).getStreamer() || field.second != i) { + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } } - reader = TypeReader(typeName, streamer); return *this; } - reader = TypeReader(typeName, streamer, fields); + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } @@ -1575,7 +1568,7 @@ QHash Bitstream::createPropertyReaders streamer = getTypeStreamers().value(property.userType()); } if (streamer) { - readers.append(PropertyReader(TypeReader(QByteArray(), streamer), property)); + readers.append(PropertyReader(streamer->getSelf(), property)); } } } @@ -1613,245 +1606,6 @@ QHash Bitstream::createPropertyWriters return propertyWriters; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(true) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash& mappings) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(ENUM_TYPE), - _bits(bits), - _mappings(mappings) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector& fields) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(STREAMABLE_TYPE), - _fields(fields) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - Type type, const TypeReaderPointer& valueReader) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(type), - _valueReader(valueReader) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(MAP_TYPE), - _keyReader(keyReader), - _valueReader(valueReader) { -} - -QVariant TypeReader::read(Bitstream& in) const { - if (_exactMatch) { - return _streamer->read(in); - } - QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); - switch (_type) { - case ENUM_TYPE: { - int value = 0; - in.read(&value, _bits); - if (_streamer) { - _streamer->setEnumValue(object, value, _mappings); - } - break; - } - case STREAMABLE_TYPE: { - foreach (const FieldReader& field, _fields) { - field.read(in, _streamer, object); - } - break; - } - case LIST_TYPE: - case SET_TYPE: { - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, value); - } - } - break; - } - case MAP_TYPE: { - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant key = _keyReader->read(in); - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, key, value); - } - } - break; - } - default: - break; - } - return object; -} - -void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - if (_exactMatch) { - _streamer->readDelta(in, object, reference); - return; - } - bool changed; - in >> changed; - if (changed) { - readRawDelta(in, object, reference); - } else { - object = reference; - } -} - -void TypeReader::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - if (_exactMatch) { - _streamer->readRawDelta(in, object, reference); - return; - } - switch (_type) { - case ENUM_TYPE: { - int value = 0; - in.read(&value, _bits); - if (_streamer) { - _streamer->setEnumValue(object, value, _mappings); - } - break; - } - case STREAMABLE_TYPE: { - foreach (const FieldReader& field, _fields) { - field.readDelta(in, _streamer, object, reference); - } - break; - } - case LIST_TYPE: { - object = reference; - int size, referenceSize; - in >> size >> referenceSize; - if (_streamer) { - if (size < referenceSize) { - _streamer->prune(object, size); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueReader->readDelta(in, value, _streamer->getValue(reference, i)); - _streamer->setValue(object, i, value); - } else { - _streamer->insert(object, _valueReader->read(in)); - } - } - } else { - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueReader->readDelta(in, value, QVariant()); - } else { - _valueReader->read(in); - } - } - } - break; - } - case SET_TYPE: { - object = reference; - int addedOrRemoved; - in >> addedOrRemoved; - for (int i = 0; i < addedOrRemoved; i++) { - QVariant value = _valueReader->read(in); - if (_streamer && !_streamer->remove(object, value)) { - _streamer->insert(object, value); - } - } - break; - } - case MAP_TYPE: { - object = reference; - int added; - in >> added; - for (int i = 0; i < added; i++) { - QVariant key = _keyReader->read(in); - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, key, value); - } - } - int modified; - in >> modified; - for (int i = 0; i < modified; i++) { - QVariant key = _keyReader->read(in); - QVariant value; - if (_streamer) { - _valueReader->readDelta(in, value, _streamer->getValue(reference, key)); - _streamer->insert(object, key, value); - } else { - _valueReader->readDelta(in, value, QVariant()); - } - } - int removed; - in >> removed; - for (int i = 0; i < removed; i++) { - QVariant key = _keyReader->read(in); - if (_streamer) { - _streamer->remove(object, key); - } - } - break; - } - default: - break; - } -} - -bool TypeReader::matchesExactly(const TypeStreamer* streamer) const { - return _exactMatch && _streamer == streamer; -} - -uint qHash(const TypeReader& typeReader, uint seed) { - return qHash(typeReader.getTypeName(), seed); -} - -QDebug& operator<<(QDebug& debug, const TypeReader& typeReader) { - return debug << typeReader.getTypeName(); -} - -FieldReader::FieldReader(const TypeReader& reader, int index) : - _reader(reader), - _index(index) { -} - -void FieldReader::read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const { - QVariant value = _reader.read(in); - if (_index != -1 && streamer) { - streamer->setField(object, _index, value); - } -} - -void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const { - QVariant value; - if (_index != -1 && streamer) { - _reader.readDelta(in, value, streamer->getField(reference, _index)); - streamer->setField(object, _index, value); - } else { - _reader.readDelta(in, value, QVariant()); - } -} - ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const PropertyReaderVector& properties) : _className(className), @@ -1887,13 +1641,13 @@ QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader) { return debug << objectReader.getClassName(); } -PropertyReader::PropertyReader(const TypeReader& reader, const QMetaProperty& property) : - _reader(reader), +PropertyReader::PropertyReader(const TypeStreamerPointer& streamer, const QMetaProperty& property) : + _streamer(streamer), _property(property) { } void PropertyReader::read(Bitstream& in, QObject* object) const { - QVariant value = _reader.read(in); + QVariant value = _streamer->read(in); if (_property.isValid() && object) { _property.write(object, value); } @@ -1901,7 +1655,7 @@ void PropertyReader::read(Bitstream& in, QObject* object) const { void PropertyReader::readDelta(Bitstream& in, QObject* object, const QObject* reference) const { QVariant value; - _reader.readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant()); + _streamer->readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant()); if (_property.isValid() && object) { _property.write(object, value); } @@ -1997,8 +1751,8 @@ QVariant TypeStreamer::getField(const QVariant& object, int index) const { return QVariant(); } -TypeReader::Type TypeStreamer::getReaderType() const { - return TypeReader::SIMPLE_TYPE; +TypeStreamer::Category TypeStreamer::getCategory() const { + return SIMPLE_CATEGORY; } int TypeStreamer::getBits() const { @@ -2076,8 +1830,8 @@ const char* EnumTypeStreamer::getName() const { return _name.constData(); } -TypeReader::Type EnumTypeStreamer::getReaderType() const { - return TypeReader::ENUM_TYPE; +TypeStreamer::Category EnumTypeStreamer::getCategory() const { + return ENUM_CATEGORY; } int EnumTypeStreamer::getBits() const { @@ -2199,6 +1953,35 @@ const TypeStreamer* GenericTypeStreamer::getStreamerToWrite(const QVariant& valu return value.value().getStreamer().data(); } +MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, + const QVector& fields) : + _baseStreamer(baseStreamer), + _fields(fields) { +} + +QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + foreach (const StreamerIndexPair& pair, _fields) { + QVariant value = pair.first->read(in); + if (pair.second != -1 && _baseStreamer) { + _baseStreamer->setField(object, pair.second, value); + } + } + return object; +} + +void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + foreach (const StreamerIndexPair& pair, _fields) { + QVariant value; + if (pair.second != -1 && _baseStreamer) { + pair.first->readDelta(in, value, _baseStreamer->getField(reference, pair.second)); + _baseStreamer->setField(object, pair.second, value); + } else { + pair.first->readDelta(in, value, QVariant()); + } + } +} + MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : _baseStreamer(baseStreamer), _valueStreamer(valueStreamer) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index cd7a0439aa..08b326d687 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -35,13 +35,11 @@ class QUrl; class Attribute; class AttributeValue; class Bitstream; -class FieldReader; class GenericValue; class ObjectReader; class OwnedAttributeValue; class PropertyReader; class PropertyWriter; -class TypeReader; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -209,7 +207,7 @@ public: class ReadMappings { public: QHash metaObjectValues; - QHash typeStreamerValues; + QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; QHash sharedObjectValues; @@ -399,7 +397,7 @@ public: Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); - Bitstream& operator>>(TypeReader& reader); + Bitstream& operator>>(TypeStreamerPointer& streamer); Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); @@ -417,7 +415,7 @@ public: Bitstream& operator>(ObjectReader& objectReader); Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(TypeReader& reader); + Bitstream& operator>(TypeStreamerPointer& reader); Bitstream& operator<(const AttributePointer& attribute); Bitstream& operator>(AttributePointer& attribute); @@ -446,7 +444,7 @@ private: GenericsMode _genericsMode; RepeatedValueStreamer _metaObjectStreamer; - RepeatedValueStreamer _typeStreamerStreamer; + RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; @@ -756,73 +754,6 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } -typedef QSharedPointer TypeReaderPointer; - -/// Contains the information required to read a type from the stream. -class TypeReader { -public: - - enum Type { SIMPLE_TYPE, ENUM_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; - - TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash& mappings); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector& fields); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, Type type, - const TypeReaderPointer& valueReader); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader); - - const QByteArray& getTypeName() const { return _typeName; } - const TypeStreamer* getStreamer() const { return _streamer; } - - QVariant read(Bitstream& in) const; - void readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - - bool matchesExactly(const TypeStreamer* streamer) const; - - bool operator==(const TypeReader& other) const { return _typeName == other._typeName; } - bool operator!=(const TypeReader& other) const { return _typeName != other._typeName; } - -private: - - QByteArray _typeName; - const TypeStreamer* _streamer; - bool _exactMatch; - Type _type; - int _bits; - QHash _mappings; - TypeReaderPointer _keyReader; - TypeReaderPointer _valueReader; - QVector _fields; -}; - -uint qHash(const TypeReader& typeReader, uint seed = 0); - -QDebug& operator<<(QDebug& debug, const TypeReader& typeReader); - -/// Contains the information required to read a metatype field from the stream and apply it. -class FieldReader { -public: - - FieldReader(const TypeReader& reader = TypeReader(), int index = -1); - - const TypeReader& getReader() const { return _reader; } - int getIndex() const { return _index; } - - void read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const; - void readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const; - -private: - - TypeReader _reader; - int _index; -}; - /// Contains the information required to read an object from the stream. class ObjectReader { public: @@ -854,16 +785,17 @@ QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader); class PropertyReader { public: - PropertyReader(const TypeReader& reader = TypeReader(), const QMetaProperty& property = QMetaProperty()); + PropertyReader(const TypeStreamerPointer& streamer = TypeStreamerPointer(), + const QMetaProperty& property = QMetaProperty()); - const TypeReader& getReader() const { return _reader; } + const TypeStreamerPointer& getStreamer() const { return _streamer; } void read(Bitstream& in, QObject* object) const; void readDelta(Bitstream& in, QObject* object, const QObject* reference) const; private: - TypeReader _reader; + TypeStreamerPointer _streamer; QMetaProperty _property; }; @@ -909,6 +841,8 @@ Q_DECLARE_METATYPE(const QMetaObject*) class TypeStreamer { public: + enum Category { SIMPLE_CATEGORY, ENUM_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, SET_CATEGORY, MAP_CATEGORY }; + virtual ~TypeStreamer(); int getType() const { return _type; } @@ -937,7 +871,7 @@ public: virtual void setField(QVariant& object, int index, const QVariant& value) const; virtual QVariant getField(const QVariant& object, int index) const; - virtual TypeReader::Type getReaderType() const; + virtual Category getCategory() const; virtual int getBits() const; virtual QMetaEnum getMetaEnum() const; @@ -992,7 +926,7 @@ public: EnumTypeStreamer(const QMetaEnum& metaEnum); virtual const char* getName() const; - virtual TypeReader::Type getReaderType() const; + virtual Category getCategory() const; virtual int getBits() const; virtual QMetaEnum getMetaEnum() const; virtual bool equal(const QVariant& first, const QVariant& second) const; @@ -1059,7 +993,7 @@ public: template class StreamableTypeStreamer : public SimpleTypeStreamer { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::STREAMABLE_CATEGORY; } virtual const QVector& getMetaFields() const { return T::getMetaFields(); } virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } virtual void setField(QVariant& object, int index, const QVariant& value) const { @@ -1068,6 +1002,23 @@ public: return static_cast(object.constData())->getField(index); } }; +typedef QPair StreamerIndexPair; + +/// A streamer class for streamables that maps to a local type. +class MappedStreamableTypeStreamer : public TypeStreamer { +public: + + MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, const QVector& fields); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +private: + + const TypeStreamer* _baseStreamer; + QVector _fields; +}; + /// Base template for collection streamers. template class CollectionTypeStreamer : public SimpleTypeStreamer { }; @@ -1076,7 +1027,7 @@ template class CollectionTypeStreamer : public SimpleTypeStreamer { template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast*>(object.data())->append(value.value()); } @@ -1092,7 +1043,7 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast*>(object.data())->append(value.value()); } @@ -1123,7 +1074,7 @@ protected: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::SET_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::SET_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast*>(object.data())->insert(value.value()); } @@ -1144,7 +1095,7 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::MAP_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const { From c045595ccbbb6228ca5287751c06198be41d0037 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 14:25:45 -0700 Subject: [PATCH 33/99] More progress on refactoring. --- libraries/metavoxels/src/Bitstream.cpp | 93 +++++++++++--------------- libraries/metavoxels/src/Bitstream.h | 3 + 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ce46429393..a6d5967fd3 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1269,8 +1269,8 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { } return *this; } - if (_genericsMode == ALL_GENERICS) { - baseStreamer = NULL; + if (!baseStreamer) { + baseStreamer = getInvalidTypeStreamer(); } switch (category) { case TypeStreamer::ENUM_CATEGORY: { @@ -1544,6 +1544,18 @@ QHash Bitstream::createEnumStreamersByName() { return enumStreamersByName; } +const TypeStreamer* Bitstream::getInvalidTypeStreamer() { + const TypeStreamer* streamer = createInvalidTypeStreamer(); + return streamer; +} + +const TypeStreamer* Bitstream::createInvalidTypeStreamer() { + TypeStreamer* streamer = new TypeStreamer(); + streamer->_type = QMetaType::UnknownType; + streamer->_self = TypeStreamerPointer(streamer); + return streamer; +} + const QHash& Bitstream::getPropertyReaders() { static QHash propertyReaders = createPropertyReaders(); return propertyReaders; @@ -1923,21 +1935,17 @@ MappedEnumTypeStreamer::MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, } QVariant MappedEnumTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); int value = 0; in.read(&value, _bits); - if (_baseStreamer) { - _baseStreamer->setEnumValue(object, value, _mappings); - } + _baseStreamer->setEnumValue(object, value, _mappings); return object; } void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { int value = 0; in.read(&value, _bits); - if (_baseStreamer) { - _baseStreamer->setEnumValue(object, value, _mappings); - } + _baseStreamer->setEnumValue(object, value, _mappings); } GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : @@ -1960,10 +1968,10 @@ MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* b } QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); foreach (const StreamerIndexPair& pair, _fields) { QVariant value = pair.first->read(in); - if (pair.second != -1 && _baseStreamer) { + if (pair.second != -1) { _baseStreamer->setField(object, pair.second, value); } } @@ -1973,7 +1981,7 @@ QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { foreach (const StreamerIndexPair& pair, _fields) { QVariant value; - if (pair.second != -1 && _baseStreamer) { + if (pair.second != -1) { pair.first->readDelta(in, value, _baseStreamer->getField(reference, pair.second)); _baseStreamer->setField(object, pair.second, value); } else { @@ -1988,14 +1996,12 @@ MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, } QVariant MappedListTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); int size; in >> size; for (int i = 0; i < size; i++) { QVariant value = _valueStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->insert(object, value); - } + _baseStreamer->insert(object, value); } return object; } @@ -2004,27 +2010,16 @@ void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const object = reference; int size, referenceSize; in >> size >> referenceSize; - if (_baseStreamer) { - if (size < referenceSize) { - _baseStreamer->prune(object, size); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); - _baseStreamer->setValue(object, i, value); - } else { - _baseStreamer->insert(object, _valueStreamer->read(in)); - } - } - } else { - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueStreamer->readDelta(in, value, QVariant()); - } else { - _valueStreamer->read(in); - } + if (size < referenceSize) { + _baseStreamer->prune(object, size); + } + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + QVariant value; + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); + _baseStreamer->setValue(object, i, value); + } else { + _baseStreamer->insert(object, _valueStreamer->read(in)); } } } @@ -2039,7 +2034,7 @@ void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const in >> addedOrRemoved; for (int i = 0; i < addedOrRemoved; i++) { QVariant value = _valueStreamer->read(in); - if (_baseStreamer && !_baseStreamer->remove(object, value)) { + if (!_baseStreamer->remove(object, value)) { _baseStreamer->insert(object, value); } } @@ -2053,15 +2048,13 @@ MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, c } QVariant MappedMapTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); int size; in >> size; for (int i = 0; i < size; i++) { QVariant key = _keyStreamer->read(in); QVariant value = _valueStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->insert(object, key, value); - } + _baseStreamer->insert(object, key, value); } return object; } @@ -2073,29 +2066,21 @@ void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const for (int i = 0; i < added; i++) { QVariant key = _keyStreamer->read(in); QVariant value = _valueStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->insert(object, key, value); - } + _baseStreamer->insert(object, key, value); } int modified; in >> modified; for (int i = 0; i < modified; i++) { QVariant key = _keyStreamer->read(in); QVariant value; - if (_baseStreamer) { - _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); - _baseStreamer->insert(object, key, value); - } else { - _valueStreamer->readDelta(in, value, QVariant()); - } + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); + _baseStreamer->insert(object, key, value); } int removed; in >> removed; for (int i = 0; i < removed; i++) { QVariant key = _keyStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->remove(object, key); - } + _baseStreamer->remove(object, key); } } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 08b326d687..434b14ce69 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -466,6 +466,9 @@ private: static const QHash& getEnumStreamersByName(); static QHash createEnumStreamersByName(); + static const TypeStreamer* getInvalidTypeStreamer(); + static const TypeStreamer* createInvalidTypeStreamer(); + static const QHash& getPropertyReaders(); static QHash createPropertyReaders(); From 82bf6002e2c3c0aaa471b8f7f5233e645cfe5174 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Jun 2014 14:57:05 -0700 Subject: [PATCH 34/99] only display the OAuthWebViewHandler when we need to ask user for authorization --- interface/src/Menu.cpp | 9 ++++++--- interface/src/Menu.h | 2 ++ interface/src/ui/OAuthWebViewHandler.cpp | 17 ++++++++++++++++- interface/src/ui/OAuthWebViewHandler.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..c2b6c293b9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -103,6 +103,7 @@ Menu::Menu() : _fastFPSAverage(ONE_SECOND_OF_FRAMES), _loginAction(NULL), _preferencesDialog(NULL), + _loginDialog(NULL), _snapshotsLocation() { Application *appInstance = Application::getInstance(); @@ -913,9 +914,11 @@ void sendFakeEnterEvent() { const float DIALOG_RATIO_OF_WINDOW = 0.30f; void Menu::loginForCurrentDomain() { - LoginDialog* loginDialog = new LoginDialog(Application::getInstance()->getWindow()); - loginDialog->show(); - loginDialog->resizeAndPosition(false); + if (!_loginDialog) { + _loginDialog = new LoginDialog(Application::getInstance()->getWindow()); + _loginDialog->show(); + _loginDialog->resizeAndPosition(false); + } } void Menu::editPreferences() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..a0fafc0514 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -27,6 +27,7 @@ #include "ui/PreferencesDialog.h" #include "ui/ChatWindow.h" #include "ui/JSConsole.h" +#include "ui/LoginDialog.h" #include "ui/ScriptEditorWindow.h" const float ADJUST_LOD_DOWN_FPS = 40.0; @@ -270,6 +271,7 @@ private: QPointer _preferencesDialog; QPointer _attachmentsDialog; QPointer _animationsDialog; + QPointer _loginDialog; QAction* _chatAction; QString _snapshotsLocation; }; diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 5415d5d9c3..5b4431bd0f 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -103,8 +103,9 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz codedAuthorizationURL.setQuery(authQuery); } + connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged); + _activeWebView->load(codedAuthorizationURL); - _activeWebView->show(); connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, this, &OAuthWebViewHandler::handleSSLErrors); @@ -137,3 +138,17 @@ void OAuthWebViewHandler::handleLoadFinished(bool success) { void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) { _webViewRedisplayTimer.restart(); } + +void OAuthWebViewHandler::handleURLChanged(const QUrl& url) { + // check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler + const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token="; + QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING); + + if (accessTokenRegex.indexIn(url.toString()) != -1) { + _activeWebView->show(); + } else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") { + // this is a login request - we're going to close the webview and signal the AccountManager that we need a login + _activeWebView->close(); + AccountManager::getInstance().checkAndSignalForAccessToken(); + } +} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index 3a30705f88..8f0c01c90d 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -32,6 +32,7 @@ private slots: void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); void handleLoadFinished(bool success); void handleWebViewDestroyed(QObject* destroyedObject); + void handleURLChanged(const QUrl& url); private: QPointer _activeWebView; QElapsedTimer _webViewRedisplayTimer; From ac0b037f0f8398fc97efbe187dd50d845e3cad44 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 15:25:00 -0700 Subject: [PATCH 35/99] removed connect(); put call into Application::nodeKilled OctreePacketReceiver::nodeKilled() doesn't get called when NodeList::nodeKilled() is emitted for some unknown reason --- interface/src/Application.cpp | 6 +++++- libraries/networking/src/LimitedNodeList.cpp | 1 + libraries/networking/src/ReceivedPacketProcessor.cpp | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a5cb6a0c12..7a7d7c10f8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,7 +244,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); - connect(NodeList::getInstance(), &NodeList::nodeKilled, &_octreeProcessor, &ReceivedPacketProcessor::nodeKilled); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&))); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); @@ -3249,6 +3248,11 @@ void Application::nodeAdded(SharedNodePointer node) { } void Application::nodeKilled(SharedNodePointer node) { + + // this is here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work: + // OctreePacketProcessor::nodeKilled is not called when NodeList::nodeKilled is emitted for some reason. + _octreeProcessor.nodeKilled(node); + if (node->getType() == NodeType::VoxelServer) { QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index c0d7941edf..e1babb24ce 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -367,6 +367,7 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { NodeHash::iterator LimitedNodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill) { qDebug() << "Killed" << *nodeItemToKill.value(); emit nodeKilled(nodeItemToKill.value()); +printf("\t\t\t emitting nodeKilled: %s\n", nodeItemToKill.value()->getUUID().toString().toLatin1().data()); return _nodeHash.erase(nodeItemToKill); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 32fc4e26f3..28a9397c60 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -51,5 +51,9 @@ bool ReceivedPacketProcessor::process() { } void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { - _nodePacketCounts.remove(node->getUUID()); + lock(); + int numRemoved = _nodePacketCounts.remove(node->getUUID()); + unlock(); + qDebug() << "RPP::killNode *************************************"; + printf("\t\t RPP::killNode: %s killed %d nodes\n", node->getUUID().toString().toLatin1().data(), numRemoved); } From 0601c24b2485370a8e01a7147f9a68ff09f42a23 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 15:26:22 -0700 Subject: [PATCH 36/99] removed debug prints --- libraries/networking/src/LimitedNodeList.cpp | 1 - libraries/networking/src/ReceivedPacketProcessor.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index e1babb24ce..c0d7941edf 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -367,7 +367,6 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { NodeHash::iterator LimitedNodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill) { qDebug() << "Killed" << *nodeItemToKill.value(); emit nodeKilled(nodeItemToKill.value()); -printf("\t\t\t emitting nodeKilled: %s\n", nodeItemToKill.value()->getUUID().toString().toLatin1().data()); return _nodeHash.erase(nodeItemToKill); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 28a9397c60..5513376c65 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -54,6 +54,4 @@ void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { lock(); int numRemoved = _nodePacketCounts.remove(node->getUUID()); unlock(); - qDebug() << "RPP::killNode *************************************"; - printf("\t\t RPP::killNode: %s killed %d nodes\n", node->getUUID().toString().toLatin1().data(), numRemoved); } From 7aef5edb8fcd7f9c2839101f33239eac8743556e Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 15:27:24 -0700 Subject: [PATCH 37/99] removed debug var --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 5513376c65..3ef518bbc2 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -52,6 +52,6 @@ bool ReceivedPacketProcessor::process() { void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { lock(); - int numRemoved = _nodePacketCounts.remove(node->getUUID()); + _nodePacketCounts.remove(node->getUUID()); unlock(); } From 6260d661f32d4645a631a332ea482f6086ca50ed Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 16:54:35 -0700 Subject: [PATCH 38/99] More work on generic containers. --- libraries/metavoxels/src/Bitstream.cpp | 259 +++++++++++++++++++------ libraries/metavoxels/src/Bitstream.h | 104 +++++++++- 2 files changed, 301 insertions(+), 62 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index a6d5967fd3..336cd36521 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -45,7 +45,9 @@ static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId()); static int genericValueStreamer = Bitstream::registerTypeStreamer( - qRegisterMetaType(), new GenericTypeStreamer()); + qRegisterMetaType(), new GenericValueStreamer()); + +static int qVariantPairListMetaTypeId = qRegisterMetaType(); IDStreamer::IDStreamer(Bitstream& stream) : _stream(stream), @@ -1173,65 +1175,10 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { } const char* typeName = streamer->getName(); *this << QByteArray::fromRawData(typeName, strlen(typeName)); - if (_metadataType == NO_METADATA) { - return *this; - } - TypeStreamer::Category category = streamer->getCategory(); - *this << (int)category; - switch (category) { - case TypeStreamer::SIMPLE_CATEGORY: - return *this; - - case TypeStreamer::ENUM_CATEGORY: { - QMetaEnum metaEnum = streamer->getMetaEnum(); - if (_metadataType == FULL_METADATA) { - *this << metaEnum.keyCount(); - for (int i = 0; i < metaEnum.keyCount(); i++) { - *this << QByteArray::fromRawData(metaEnum.key(i), strlen(metaEnum.key(i))); - *this << metaEnum.value(i); - } - } else { - *this << streamer->getBits(); - QCryptographicHash hash(QCryptographicHash::Md5); - for (int i = 0; i < metaEnum.keyCount(); i++) { - hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); - qint32 value = metaEnum.value(i); - hash.addData((const char*)&value, sizeof(qint32)); - } - QByteArray hashResult = hash.result(); - write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } - return *this; - } - case TypeStreamer::LIST_CATEGORY: - case TypeStreamer::SET_CATEGORY: - return *this << streamer->getValueStreamer(); - - case TypeStreamer::MAP_CATEGORY: - return *this << streamer->getKeyStreamer() << streamer->getValueStreamer(); - - default: - break; // fall through - } - // streamable type - const QVector& metaFields = streamer->getMetaFields(); - *this << metaFields.size(); - if (metaFields.isEmpty()) { - return *this; - } - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const MetaField& metaField, metaFields) { - _typeStreamerStreamer << metaField.getStreamer(); - if (_metadataType == FULL_METADATA) { - *this << metaField.getName(); - } else { - hash.addData(metaField.getName().constData(), metaField.getName().size() + 1); - } - } - if (_metadataType == HASH_METADATA) { - QByteArray hashResult = hash.result(); - write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } + if (_metadataType != NO_METADATA) { + *this << (int)streamer->getCategory(); + streamer->writeMetadata(*this, _metadataType == FULL_METADATA); + } return *this; } @@ -1703,6 +1650,31 @@ const TypeStreamer* TypeStreamer::getStreamerToWrite(const QVariant& value) cons return this; } +void TypeStreamer::writeMetadata(Bitstream& out, bool full) const { + if (getCategory() != STREAMABLE_CATEGORY) { + return; + } + // streamable type + const QVector& metaFields = getMetaFields(); + out << metaFields.size(); + if (metaFields.isEmpty()) { + return; + } + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const MetaField& metaField, metaFields) { + out << metaField.getStreamer(); + if (full) { + out << metaField.getName(); + } else { + hash.addData(metaField.getName().constData(), metaField.getName().size() + 1); + } + } + if (!full) { + QByteArray hashResult = hash.result(); + out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } +} + bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const { return first == second; } @@ -1842,6 +1814,27 @@ const char* EnumTypeStreamer::getName() const { return _name.constData(); } +void EnumTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + QMetaEnum metaEnum = getMetaEnum(); + if (full) { + out << metaEnum.keyCount(); + for (int i = 0; i < metaEnum.keyCount(); i++) { + out << QByteArray::fromRawData(metaEnum.key(i), strlen(metaEnum.key(i))); + out << metaEnum.value(i); + } + } else { + out << getBits(); + QCryptographicHash hash(QCryptographicHash::Md5); + for (int i = 0; i < metaEnum.keyCount(); i++) { + hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); + qint32 value = metaEnum.value(i); + hash.addData((const char*)&value, sizeof(qint32)); + } + QByteArray hashResult = hash.result(); + out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } +} + TypeStreamer::Category EnumTypeStreamer::getCategory() const { return ENUM_CATEGORY; } @@ -1948,6 +1941,54 @@ void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const _baseStreamer->setEnumValue(object, value, _mappings); } +GenericTypeStreamer::GenericTypeStreamer(const QByteArray& name) : + _name(name) { +} + +const char* GenericTypeStreamer::getName() const { + return _name.constData(); +} + +GenericEnumTypeStreamer::GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, + int bits, const QByteArray& hash) : + GenericTypeStreamer(name), + _values(values), + _bits(bits), + _hash(hash) { + + _type = qMetaTypeId(); +} + +void GenericEnumTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + if (full) { + out << _values.size(); + foreach (const NameIntPair& value, _values) { + out << value.first << value.second; + } + } else { + out << _bits; + if (_hash.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const NameIntPair& value, _values) { + hash.addData(value.first.constData(), value.first.size() + 1); + qint32 intValue = value.second; + hash.addData((const char*)&intValue, sizeof(qint32)); + } + const_cast(this)->_hash = hash.result(); + } + out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); + } +} + +void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const { + int intValue = value.toInt(); + out.write(&intValue, _bits); +} + +TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { + return ENUM_CATEGORY; +} + GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : _streamer(streamer), _value(value) { @@ -1957,7 +1998,7 @@ bool GenericValue::operator==(const GenericValue& other) const { return _streamer == other._streamer && _value == other._value; } -const TypeStreamer* GenericTypeStreamer::getStreamerToWrite(const QVariant& value) const { +const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { return value.value().getStreamer().data(); } @@ -1990,6 +2031,46 @@ void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, } } +GenericStreamableTypeStreamer::GenericStreamableTypeStreamer(const QByteArray& name, + const QVector& fields, const QByteArray& hash) : + GenericTypeStreamer(name), + _fields(fields), + _hash(hash) { + + _type = qMetaTypeId(); +} + +void GenericStreamableTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _fields.size(); + foreach (const StreamerNamePair& field, _fields) { + out << field.first.data(); + if (full) { + out << field.second; + } + } + if (!full) { + if (_hash.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const StreamerNamePair& field, _fields) { + hash.addData(field.second.constData(), field.second.size() + 1); + } + const_cast(this)->_hash = hash.result(); + } + out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); + } +} + +void GenericStreamableTypeStreamer::write(Bitstream& out, const QVariant& value) const { + QVariantList values = value.toList(); + for (int i = 0; i < _fields.size(); i++) { + _fields.at(i).first->write(out, values.at(i)); + } +} + +TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { + return STREAMABLE_CATEGORY; +} + MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : _baseStreamer(baseStreamer), _valueStreamer(valueStreamer) { @@ -2024,6 +2105,29 @@ void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const } } +GenericListTypeStreamer::GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer) : + GenericTypeStreamer(name), + _valueStreamer(valueStreamer) { + + _type = qMetaTypeId(); +} + +void GenericListTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _valueStreamer.data(); +} + +void GenericListTypeStreamer::write(Bitstream& out, const QVariant& value) const { + QVariantList values = value.toList(); + out << values.size(); + foreach (const QVariant& element, values) { + _valueStreamer->write(out, element); + } +} + +TypeStreamer::Category GenericListTypeStreamer::getCategory() const { + return LIST_CATEGORY; +} + MappedSetTypeStreamer::MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : MappedListTypeStreamer(baseStreamer, valueStreamer) { } @@ -2040,6 +2144,14 @@ void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const } } +GenericSetTypeStreamer::GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer) : + GenericListTypeStreamer(name, valueStreamer) { +} + +TypeStreamer::Category GenericSetTypeStreamer::getCategory() const { + return SET_CATEGORY; +} + MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, const TypeStreamerPointer& valueStreamer) : _baseStreamer(baseStreamer), @@ -2084,3 +2196,28 @@ void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const } } +GenericMapTypeStreamer::GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer) : + GenericTypeStreamer(name), + _keyStreamer(keyStreamer), + _valueStreamer(valueStreamer) { + + _type = qMetaTypeId(); +} + +void GenericMapTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _keyStreamer.data() << _valueStreamer.data(); +} + +void GenericMapTypeStreamer::write(Bitstream& out, const QVariant& value) const { + QVariantPairList values = value.value(); + out << values.size(); + foreach (const QVariantPair& pair, values) { + _keyStreamer->write(out, pair.first); + _valueStreamer->write(out, pair.second); + } +} + +TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { + return MAP_CATEGORY; +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 434b14ce69..6a3fa88c94 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -45,10 +45,16 @@ class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; +typedef QPair NameIntPair; typedef QSharedPointer TypeStreamerPointer; typedef QVector PropertyReaderVector; typedef QVector PropertyWriterVector; +typedef QPair QVariantPair; +typedef QList QVariantPairList; + +Q_DECLARE_METATYPE(QVariantPairList) + /// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that /// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows /// us to use the minimum number of bits to encode the IDs. @@ -856,6 +862,8 @@ public: virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual bool equal(const QVariant& first, const QVariant& second) const; virtual void write(Bitstream& out, const QVariant& value) const; @@ -929,6 +937,7 @@ public: EnumTypeStreamer(const QMetaEnum& metaEnum); virtual const char* getName() const; + virtual void writeMetadata(Bitstream& out, bool full) const; virtual Category getCategory() const; virtual int getBits() const; virtual QMetaEnum getMetaEnum() const; @@ -966,6 +975,36 @@ private: QHash _mappings; }; +/// Base class for generic type streamers, which contain all the metadata required to write out a type. +class GenericTypeStreamer : public TypeStreamer { +public: + + GenericTypeStreamer(const QByteArray& name); + + virtual const char* getName() const; + +private: + + QByteArray _name; +}; + +/// A streamer for generic enums. +class GenericEnumTypeStreamer : public GenericTypeStreamer { +public: + + GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, int bits, const QByteArray& hash); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + QVector _values; + int _bits; + QByteArray _hash; +}; + /// Contains a value along with a pointer to its streamer. class GenericValue { public: @@ -986,7 +1025,7 @@ private: Q_DECLARE_METATYPE(GenericValue) /// A streamer class for generic values. -class GenericTypeStreamer : public SimpleTypeStreamer { +class GenericValueStreamer : public SimpleTypeStreamer { public: virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; @@ -1022,6 +1061,24 @@ private: QVector _fields; }; +typedef QPair StreamerNamePair; + +/// A streamer for generic enums. +class GenericStreamableTypeStreamer : public GenericTypeStreamer { +public: + + GenericStreamableTypeStreamer(const QByteArray& name, const QVector& fields, const QByteArray& hash); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + QVector _fields; + QByteArray _hash; +}; + /// Base template for collection streamers. template class CollectionTypeStreamer : public SimpleTypeStreamer { }; @@ -1030,6 +1087,7 @@ template class CollectionTypeStreamer : public SimpleTypeStreamer { template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { @@ -1046,6 +1104,7 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { @@ -1073,10 +1132,26 @@ protected: TypeStreamerPointer _valueStreamer; }; +/// A streamer for generic lists. +class GenericListTypeStreamer : public GenericTypeStreamer { +public: + + GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + TypeStreamerPointer _valueStreamer; +}; + /// A streamer for set types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::SET_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { @@ -1094,10 +1169,20 @@ public: virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; }; +/// A streamer for generic sets. +class GenericSetTypeStreamer : public GenericListTypeStreamer { +public: + + GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer); + + virtual Category getCategory() const; +}; + /// A streamer for hash types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getKeyStreamer() << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } @@ -1126,6 +1211,23 @@ private: TypeStreamerPointer _valueStreamer; }; +/// A streamer for generic maps. +class GenericMapTypeStreamer : public GenericTypeStreamer { +public: + + GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + TypeStreamerPointer _keyStreamer; + TypeStreamerPointer _valueStreamer; +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); From eff097638ce6c12e700e561331594b8b6976f986 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:02:08 -0700 Subject: [PATCH 39/99] Implementd sixense mouse emulation --- interface/src/Application.cpp | 10 +++- interface/src/devices/SixenseManager.cpp | 68 +++++++++++++++++++++ interface/src/devices/SixenseManager.h | 9 +++ interface/src/ui/ApplicationOverlay.cpp | 75 +++++++++++++++++++++++- 4 files changed, 159 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b3256141e..6035cdd7cb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1090,6 +1090,13 @@ void Application::focusOutEvent(QFocusEvent* event) { } void Application::mouseMoveEvent(QMouseEvent* event) { + + bool showMouse = true; + // If this mouse move event is emitted by a controller, dont show the mouse cursor + if (event->type() == CONTROLLER_MOVE_EVENT) { + showMouse = false; + } + _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -1097,10 +1104,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) { return; } - _lastMouseMove = usecTimestampNow(); - if (_mouseHidden && !OculusManager::isConnected()) { + if (_mouseHidden && showMouse && !OculusManager::isConnected()) { getGLWidget()->setCursor(Qt::ArrowCursor); _mouseHidden = false; _seenMouseMove = true; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 8cd3cc059e..4ecb416a92 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -39,6 +39,10 @@ SixenseManager::SixenseManager() { sixenseInit(); #endif + _triggerPressed = false; + _bumperPressed = false; + _oldPos.setX(-1); + _oldPos.setY(-1); } SixenseManager::~SixenseManager() { @@ -107,6 +111,12 @@ void SixenseManager::update(float deltaTime) { palm->setTrigger(data->trigger); palm->setJoystick(data->joystick_x, data->joystick_y); + + // Emulate the mouse so we can use scripts + if (numActiveControllers == 2) { + emulateMouse(palm); + } + // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); position *= METERS_PER_MILLIMETER; @@ -313,5 +323,63 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) } } } + +//Injecting mouse movements and clicks +void SixenseManager::emulateMouse(PalmData *palm) { + MyAvatar* avatar = Application::getInstance()->getAvatar(); + QPoint pos; + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = Application::getInstance()->getGLWidget()->width(); + + pos.setX(cursorRange * xAngle); + pos.setY(cursorRange * yAngle); + + //If position has changed, emit a mouse move to the application + if (pos.x() != _oldPos.x() || pos.y() != _oldPos.y()) { + QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); + + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + _oldPos = pos; + + //Check for bumper press ( Right Click ) + if (palm->getControllerButtons() & BUTTON_FWD) { + if (!_bumperPressed) { + _bumperPressed = true; + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0); + + Application::getInstance()->mousePressEvent(&mouseEvent); + } + } else if (_bumperPressed) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _bumperPressed = false; + } + + //Check for trigger press ( Left Click ) + if (palm->getTrigger() == 1.0f) { + if (!_triggerPressed) { + _triggerPressed = true; + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0); + + Application::getInstance()->mousePressEvent(&mouseEvent); + } + } else if (_triggerPressed) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _triggerPressed = false; + } +} + #endif // HAVE_SIXENSE diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index a98d4c0e4e..061b3cc625 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -27,6 +27,9 @@ const unsigned int BUTTON_3 = 1U << 3; const unsigned int BUTTON_4 = 1U << 4; const unsigned int BUTTON_FWD = 1U << 7; +// Event type that represents moving the controller +const unsigned int CONTROLLER_MOVE_EVENT = 1500U; + /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { Q_OBJECT @@ -44,6 +47,7 @@ public slots: private: #ifdef HAVE_SIXENSE void updateCalibration(const sixenseControllerData* controllers); + void emulateMouse(PalmData *palm); int _calibrationState; @@ -65,6 +69,11 @@ private: #endif quint64 _lastMovement; glm::vec3 _amountMoved; + + // for mouse emulation + bool _triggerPressed; + bool _bumperPressed; + QPoint _oldPos; }; #endif // hifi_SixenseManager_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a48e0a2c1a..750be96cc9 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -35,6 +35,78 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +void renderControllerPointer() +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); + int numberOfPalms = handData->getNumPalms(); + + + int palmIndex = 3; + const PalmData* palmData = NULL; + + if (palmIndex >= handData->getPalms().size()) { + return; + } + + if (handData->getPalms()[palmIndex].isActive()) { + palmData = &handData->getPalms()[palmIndex]; + } else { + return; + } + + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = glWidget->width(); + + int mouseX = cursorRange * xAngle; + int mouseY = cursorRange * yAngle; + + if (mouseX < 0) { + mouseX = 0; + } else if (mouseX > glWidget->width()) { + mouseX = glWidget->width(); + } + if (mouseY < 0) { + mouseY = 0; + } else if (mouseY > glWidget->width()) { + mouseY = glWidget->width(); + } + + const float pointerWidth = 40; + const float pointerHeight = 40; + const float crossPad = 16; + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + glBegin(GL_QUADS); + + glColor3f(0, 0, 1); + + //Horizontal crosshair + glVertex2i(mouseX, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); + glVertex2i(mouseX, mouseY - pointerHeight + crossPad); + + //Vertical crosshair + glVertex2i(mouseX + crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); + glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + + glEnd(); +} + // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); @@ -254,8 +326,9 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(mouseX + crossPad, mouseY - pointerHeight); glEnd(); + } else { //only render controller pointer if we aren't already rendering a mouse pointer + renderControllerPointer(); } - glPopMatrix(); glMatrixMode(GL_MODELVIEW); From 2fec7400e2d06d2f9f730d32f42db8be802ec511 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:21:59 -0700 Subject: [PATCH 40/99] Added menu options for sixense mouse input --- interface/src/Menu.cpp | 5 ++++ interface/src/Menu.h | 3 ++ interface/src/devices/SixenseManager.cpp | 35 ++++++++++++++++++++---- interface/src/ui/ApplicationOverlay.cpp | 10 +++++-- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..f78b8a822f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -379,6 +379,11 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false); addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true); + QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLeftHanded, 0, false); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); + QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..40c6e646d9 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -397,6 +397,9 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; + const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; + const QString SixenseLeftHanded = "Left Handed Sixense Mouse Input"; + const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; const QString ShowBordersParticleNodes = "Show Particle Nodes"; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 4ecb416a92..f347d09af9 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -113,8 +113,16 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts - if (numActiveControllers == 2) { - emulateMouse(palm); + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { + if (numActiveControllers == 1){ + emulateMouse(palm); + } + } else { + if (numActiveControllers == 2) { + emulateMouse(palm); + } + } } // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. @@ -348,16 +356,28 @@ void SixenseManager::emulateMouse(PalmData *palm) { } _oldPos = pos; + Qt::MouseButton bumperButton; + Qt::MouseButton triggerButton; + + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseInvertInputButtons)) { + bumperButton = Qt::LeftButton; + triggerButton = Qt::RightButton; + } else { + bumperButton = Qt::RightButton; + triggerButton = Qt::LeftButton; + } + //Check for bumper press ( Right Click ) if (palm->getControllerButtons() & BUTTON_FWD) { if (!_bumperPressed) { _bumperPressed = true; - QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0); + + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } } else if (_bumperPressed) { - QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); @@ -368,17 +388,20 @@ void SixenseManager::emulateMouse(PalmData *palm) { if (palm->getTrigger() == 1.0f) { if (!_triggerPressed) { _triggerPressed = true; - QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0); + + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } } else if (_triggerPressed) { - QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); _triggerPressed = false; } + + } #endif // HAVE_SIXENSE diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 750be96cc9..401062bb36 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -45,7 +45,12 @@ void renderControllerPointer() int numberOfPalms = handData->getNumPalms(); - int palmIndex = 3; + int palmIndex; + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { + palmIndex = 2; + } else { + palmIndex = 3; + } const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -326,7 +331,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(mouseX + crossPad, mouseY - pointerHeight); glEnd(); - } else { //only render controller pointer if we aren't already rendering a mouse pointer + } else if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } glPopMatrix(); From d993fd52f2d678e02fed0aafb5142b218634f13f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:27:00 -0700 Subject: [PATCH 41/99] Cleaned up a small bit of code --- interface/src/devices/SixenseManager.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index f347d09af9..2a0db69d52 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -114,14 +114,9 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { - if (numActiveControllers == 1){ - emulateMouse(palm); - } - } else { - if (numActiveControllers == 2) { - emulateMouse(palm); - } + // Check if we are on the correct palm + if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { + emulateMouse(palm); } } From 14a591c5d853631102e6003cbae2f108bfabc43f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:35:50 -0700 Subject: [PATCH 42/99] Move to previous line --- interface/src/ui/ApplicationOverlay.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 401062bb36..924c2e9d84 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -35,8 +35,7 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -void renderControllerPointer() -{ +void renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); From 32a9eed88d8728f1221b14f6799a0cda672305bd Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 18:05:10 -0700 Subject: [PATCH 43/99] Fixed build failure --- interface/src/devices/SixenseManager.cpp | 9 +++++---- interface/src/devices/SixenseManager.h | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 2a0db69d52..abbb32ec5e 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -41,8 +41,8 @@ SixenseManager::SixenseManager() { #endif _triggerPressed = false; _bumperPressed = false; - _oldPos.setX(-1); - _oldPos.setY(-1); + _oldX = -1; + _oldY = -1; } SixenseManager::~SixenseManager() { @@ -344,12 +344,13 @@ void SixenseManager::emulateMouse(PalmData *palm) { pos.setY(cursorRange * yAngle); //If position has changed, emit a mouse move to the application - if (pos.x() != _oldPos.x() || pos.y() != _oldPos.y()) { + if (pos.x() != _oldX || pos.y() != _oldY) { QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); Application::getInstance()->mouseMoveEvent(&mouseEvent); } - _oldPos = pos; + _oldX = pos.x(); + _oldY = pos.y(); Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 061b3cc625..93888ce944 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -73,7 +73,8 @@ private: // for mouse emulation bool _triggerPressed; bool _bumperPressed; - QPoint _oldPos; + int _oldX; + int _oldY; }; #endif // hifi_SixenseManager_h From 6c98248e507cc6a29a35bce8c7176b1351bd0ff8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 18:30:01 -0700 Subject: [PATCH 44/99] More progress on generics. --- libraries/metavoxels/src/Bitstream.cpp | 193 ++++++++++++++++++++----- libraries/metavoxels/src/Bitstream.h | 12 +- 2 files changed, 164 insertions(+), 41 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 336cd36521..d285e4aa3f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1216,7 +1216,15 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { } return *this; } + if (_genericsMode == ALL_GENERICS) { + streamer = readGenericTypeStreamer(typeName, category); + return *this; + } if (!baseStreamer) { + if (_genericsMode == FALLBACK_GENERICS) { + streamer = readGenericTypeStreamer(typeName, category); + return *this; + } baseStreamer = getInvalidTypeStreamer(); } switch (category) { @@ -1224,8 +1232,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { if (_metadataType == FULL_METADATA) { int keyCount; *this >> keyCount; - QMetaEnum metaEnum = (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) ? - baseStreamer->getMetaEnum() : QMetaEnum(); + QMetaEnum metaEnum = baseStreamer->getMetaEnum(); QHash mappings; bool matches = (keyCount == metaEnum.keyCount()); int highestValue = 0; @@ -1248,7 +1255,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { int bits; *this >> bits; QCryptographicHash hash(QCryptographicHash::Md5); - if (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { + if (baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { QMetaEnum metaEnum = baseStreamer->getMetaEnum(); for (int i = 0; i < metaEnum.keyCount(); i++) { hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); @@ -1269,8 +1276,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { case TypeStreamer::SET_CATEGORY: { TypeStreamerPointer valueStreamer; *this >> valueStreamer; - if (!(baseStreamer && baseStreamer->getCategory() == category && - valueStreamer == baseStreamer->getValueStreamer())) { + if (!(baseStreamer->getCategory() == category && valueStreamer == baseStreamer->getValueStreamer())) { streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? new MappedListTypeStreamer(baseStreamer, valueStreamer) : new MappedSetTypeStreamer(baseStreamer, valueStreamer)); @@ -1280,7 +1286,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { case TypeStreamer::MAP_CATEGORY: { TypeStreamerPointer keyStreamer, valueStreamer; *this >> keyStreamer >> valueStreamer; - if (!(baseStreamer && baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && + if (!(baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer())) { streamer = TypeStreamerPointer(new MappedMapTypeStreamer(baseStreamer, keyStreamer, valueStreamer)); } @@ -1298,9 +1304,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { if (_metadataType == FULL_METADATA) { QByteArray fieldName; *this >> fieldName; - if (baseStreamer) { - index = baseStreamer->getFieldIndex(fieldName); - } + index = baseStreamer->getFieldIndex(fieldName); } fields[i] = StreamerIndexPair(typeStreamer, index); } @@ -1308,49 +1312,44 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { if (_metadataType == HASH_METADATA) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; - if (baseStreamer) { - const QVector& localFields = baseStreamer->getMetaFields(); - if (fieldCount != localFields.size()) { - matches = false; - - } else { - if (fieldCount == 0) { - return *this; - } - for (int i = 0; i < fieldCount; i++) { - const MetaField& localField = localFields.at(i); - if (fields.at(i).first != localField.getStreamer()) { - matches = false; - break; - } - hash.addData(localField.getName().constData(), localField.getName().size() + 1); - } + const QVector& localFields = baseStreamer->getMetaFields(); + if (fieldCount != localFields.size()) { + matches = false; + + } else { + if (fieldCount == 0) { + return *this; } + for (int i = 0; i < fieldCount; i++) { + const MetaField& localField = localFields.at(i); + if (fields.at(i).first != localField.getStreamer()) { + matches = false; + break; + } + hash.addData(localField.getName().constData(), localField.getName().size() + 1); + } } QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (baseStreamer && matches && localHashResult == remoteHashResult) { + if (matches && localHashResult == remoteHashResult) { // since everything is the same, we can use the default streamer return *this; } - } else if (baseStreamer) { - // if all fields are the same type and in the right order, we can use the (more efficient) default streamer - const QVector& localFields = baseStreamer->getMetaFields(); - if (fieldCount != localFields.size()) { + } + // if all fields are the same type and in the right order, we can use the (more efficient) default streamer + const QVector& localFields = baseStreamer->getMetaFields(); + if (fieldCount != localFields.size()) { + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); + return *this; + } + for (int i = 0; i < fieldCount; i++) { + const StreamerIndexPair& field = fields.at(i); + if (field.first != localFields.at(i).getStreamer() || field.second != i) { streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } - for (int i = 0; i < fieldCount; i++) { - const StreamerIndexPair& field = fields.at(i); - if (field.first != localFields.at(i).getStreamer() || field.second != i) { - streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); - return *this; - } - } - return *this; } - streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } @@ -1444,6 +1443,81 @@ void Bitstream::clearSharedObject(QObject* object) { } } +const int MD5_HASH_SIZE = 16; + +TypeStreamerPointer Bitstream::readGenericTypeStreamer(const QByteArray& name, int category) { + TypeStreamerPointer streamer; + switch (category) { + case TypeStreamer::ENUM_CATEGORY: { + QVector values; + int bits; + QByteArray hash; + if (_metadataType == FULL_METADATA) { + int keyCount; + *this >> keyCount; + values.resize(keyCount); + int highestValue = 0; + for (int i = 0; i < keyCount; i++) { + QByteArray name; + int value; + *this >> name >> value; + values[i] = NameIntPair(name, value); + highestValue = qMax(highestValue, value); + } + bits = getBitsForHighestValue(highestValue); + + } else { + *this >> bits; + hash.resize(MD5_HASH_SIZE); + read(hash.data(), hash.size() * BITS_IN_BYTE); + } + streamer = TypeStreamerPointer(new GenericEnumTypeStreamer(name, values, bits, hash)); + break; + } + case TypeStreamer::STREAMABLE_CATEGORY: { + int fieldCount; + *this >> fieldCount; + QVector fields(fieldCount); + QByteArray hash; + if (fieldCount == 0) { + streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(name, fields, hash)); + break; + } + for (int i = 0; i < fieldCount; i++) { + TypeStreamerPointer streamer; + *this >> streamer; + QByteArray name; + if (_metadataType == FULL_METADATA) { + *this >> name; + } + fields[i] = StreamerNamePair(streamer, name); + } + if (_metadataType == HASH_METADATA) { + hash.resize(MD5_HASH_SIZE); + read(hash.data(), hash.size() * BITS_IN_BYTE); + } + streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(name, fields, hash)); + break; + } + case TypeStreamer::LIST_CATEGORY: + case TypeStreamer::SET_CATEGORY: { + TypeStreamerPointer valueStreamer; + *this >> valueStreamer; + streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? + new GenericListTypeStreamer(name, valueStreamer) : new GenericSetTypeStreamer(name, valueStreamer)); + break; + } + case TypeStreamer::MAP_CATEGORY: { + TypeStreamerPointer keyStreamer, valueStreamer; + *this >> keyStreamer >> valueStreamer; + streamer = TypeStreamerPointer(new GenericMapTypeStreamer(name, keyStreamer, valueStreamer)); + break; + } + } + static_cast(streamer.data())->_weakSelf = streamer; + return streamer; +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; @@ -1985,6 +2059,12 @@ void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const out.write(&intValue, _bits); } +QVariant GenericEnumTypeStreamer::read(Bitstream& in) const { + int intValue = 0; + in.read(&intValue, _bits); + return QVariant::fromValue(GenericValue(_weakSelf, intValue)); +} + TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { return ENUM_CATEGORY; } @@ -2042,6 +2122,9 @@ GenericStreamableTypeStreamer::GenericStreamableTypeStreamer(const QByteArray& n void GenericStreamableTypeStreamer::writeMetadata(Bitstream& out, bool full) const { out << _fields.size(); + if (_fields.isEmpty()) { + return; + } foreach (const StreamerNamePair& field, _fields) { out << field.first.data(); if (full) { @@ -2067,6 +2150,14 @@ void GenericStreamableTypeStreamer::write(Bitstream& out, const QVariant& value) } } +QVariant GenericStreamableTypeStreamer::read(Bitstream& in) const { + QVariantList values; + foreach (const StreamerNamePair& field, _fields) { + values.append(field.first->read(in)); + } + return QVariant::fromValue(GenericValue(_weakSelf, values)); +} + TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { return STREAMABLE_CATEGORY; } @@ -2124,6 +2215,16 @@ void GenericListTypeStreamer::write(Bitstream& out, const QVariant& value) const } } +QVariant GenericListTypeStreamer::read(Bitstream& in) const { + QVariantList values; + int size; + in >> size; + for (int i = 0; i < size; i++) { + values.append(_valueStreamer->read(in)); + } + return QVariant::fromValue(GenericValue(_weakSelf, values)); +} + TypeStreamer::Category GenericListTypeStreamer::getCategory() const { return LIST_CATEGORY; } @@ -2218,6 +2319,18 @@ void GenericMapTypeStreamer::write(Bitstream& out, const QVariant& value) const } } +QVariant GenericMapTypeStreamer::read(Bitstream& in) const { + QVariantPairList values; + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value = _valueStreamer->read(in); + values.append(QVariantPair(key, value)); + } + return QVariant::fromValue(GenericValue(_weakSelf, QVariant::fromValue(values))); +} + TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { return MAP_CATEGORY; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 6a3fa88c94..ee9b8b472c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -47,6 +47,7 @@ typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; typedef QPair NameIntPair; typedef QSharedPointer TypeStreamerPointer; +typedef QWeakPointer WeakTypeStreamerPointer; typedef QVector PropertyReaderVector; typedef QVector PropertyWriterVector; @@ -442,6 +443,8 @@ private slots: private: + TypeStreamerPointer readGenericTypeStreamer(const QByteArray& name, int category); + QDataStream& _underlying; quint8 _byte; int _position; @@ -983,9 +986,12 @@ public: virtual const char* getName() const; -private: +protected: + + friend class Bitstream; QByteArray _name; + WeakTypeStreamerPointer _weakSelf; }; /// A streamer for generic enums. @@ -996,6 +1002,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: @@ -1071,6 +1078,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: @@ -1140,6 +1148,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: @@ -1220,6 +1229,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: From 1503946b7c0046ca751c4487d0bf65287395f567 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Jun 2014 21:11:49 -0700 Subject: [PATCH 45/99] Fix build warning in Audio.cpp \interface\src\Audio.cpp(309): warning C4018: '<' : signed/unsigned mismatch --- interface/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ebc228a13d..2cbe3a062f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -306,7 +306,7 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples, } else { // this is a 48 to 24 resampling but both source and destination are two channels // squish two samples into one in each channel - for (int i = 0; i < numSourceSamples; i += 4) { + for (unsigned int i = 0; i < numSourceSamples; i += 4) { destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2); destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2); } From d0b1880ee4e65a87a3c78d14ab0bfaae2ed17604 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 10:20:35 -0700 Subject: [PATCH 46/99] Experimenting with ambidextrous sixense mouse input --- interface/src/devices/SixenseManager.cpp | 96 ++++++++++------- interface/src/devices/SixenseManager.h | 12 +-- interface/src/ui/ApplicationOverlay.cpp | 126 ++++++++++++----------- 3 files changed, 132 insertions(+), 102 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index abbb32ec5e..37bad28093 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -39,10 +39,14 @@ SixenseManager::SixenseManager() { sixenseInit(); #endif - _triggerPressed = false; - _bumperPressed = false; - _oldX = -1; - _oldY = -1; + _triggerPressed[0] = false; + _bumperPressed[0] = false; + _oldX[0] = -1; + _oldY[0] = -1; + _triggerPressed[1] = false; + _bumperPressed[1] = false; + _oldX[1] = -1; + _oldY[1] = -1; } SixenseManager::~SixenseManager() { @@ -115,9 +119,9 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { // Check if we are on the correct palm - if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { - emulateMouse(palm); - } + //if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { + emulateMouse(palm, numActiveControllers - 1); + //} } // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. @@ -328,30 +332,13 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) } //Injecting mouse movements and clicks -void SixenseManager::emulateMouse(PalmData *palm) { +void SixenseManager::emulateMouse(PalmData* palm, int index) { MyAvatar* avatar = Application::getInstance()->getAvatar(); + QGLWidget* widget = Application::getInstance()->getGLWidget(); QPoint pos; // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - - float cursorRange = Application::getInstance()->getGLWidget()->width(); - - pos.setX(cursorRange * xAngle); - pos.setY(cursorRange * yAngle); - - //If position has changed, emit a mouse move to the application - if (pos.x() != _oldX || pos.y() != _oldY) { - QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); - - Application::getInstance()->mouseMoveEvent(&mouseEvent); - } - _oldX = pos.x(); - _oldY = pos.y(); - Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; @@ -362,42 +349,79 @@ void SixenseManager::emulateMouse(PalmData *palm) { bumperButton = Qt::RightButton; triggerButton = Qt::LeftButton; } + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = widget->width(); + + + pos.setX(cursorRange * xAngle); + pos.setY(cursorRange * yAngle); + + //If we are off screen then we should stop processing, and if a trigger or bumper is pressed, + //we should unpress them. + if (pos.x() < 0 || pos.x() > widget->width() || pos.y() < 0 || pos.y() > widget->height()) { + if (_bumperPressed[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _bumperPressed[index] = false; + } + if (_triggerPressed[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _triggerPressed[index] = false; + } + return; + } + + //If position has changed, emit a mouse move to the application + if (pos.x() != _oldX[index] || pos.y() != _oldY[index]) { + QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); + + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + _oldX[index] = pos.x(); + _oldY[index] = pos.y(); //Check for bumper press ( Right Click ) if (palm->getControllerButtons() & BUTTON_FWD) { - if (!_bumperPressed) { - _bumperPressed = true; + if (!_bumperPressed[index]) { + _bumperPressed[index] = true; QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } - } else if (_bumperPressed) { + } else if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); - _bumperPressed = false; + _bumperPressed[index] = false; } //Check for trigger press ( Left Click ) if (palm->getTrigger() == 1.0f) { - if (!_triggerPressed) { - _triggerPressed = true; + if (!_triggerPressed[index]) { + _triggerPressed[index] = true; QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } - } else if (_triggerPressed) { + } else if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); - _triggerPressed = false; + _triggerPressed[index] = false; } - - } #endif // HAVE_SIXENSE diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 93888ce944..f3c5633f90 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -47,7 +47,7 @@ public slots: private: #ifdef HAVE_SIXENSE void updateCalibration(const sixenseControllerData* controllers); - void emulateMouse(PalmData *palm); + void emulateMouse(PalmData* palm, int index); int _calibrationState; @@ -70,11 +70,11 @@ private: quint64 _lastMovement; glm::vec3 _amountMoved; - // for mouse emulation - bool _triggerPressed; - bool _bumperPressed; - int _oldX; - int _oldY; + // for mouse emulation with the two controllers + bool _triggerPressed[2]; + bool _bumperPressed[2]; + int _oldX[2]; + int _oldY[2]; }; #endif // hifi_SixenseManager_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 924c2e9d84..1deab117ac 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -45,70 +45,76 @@ void renderControllerPointer() { int palmIndex; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { + /*if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { palmIndex = 2; } else { palmIndex = 3; + }*/ + for (palmIndex = 2; palmIndex < 4; palmIndex++) { + const PalmData* palmData = NULL; + + if (palmIndex >= handData->getPalms().size()) { + return; + } + + if (handData->getPalms()[palmIndex].isActive()) { + palmData = &handData->getPalms()[palmIndex]; + } else { + return; + } + + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = glWidget->width(); + + int mouseX = cursorRange * xAngle; + int mouseY = cursorRange * yAngle; + + if (mouseX < 0) { + //mouseX = 0; + continue; + } else if (mouseX > glWidget->width()) { + //mouseX = glWidget->width(); + continue; + } + if (mouseY < 0) { + //mouseY = 0; + continue; + } else if (mouseY > glWidget->width()) { + //mouseY = glWidget->width(); + continue; + } + + const float pointerWidth = 40; + const float pointerHeight = 40; + const float crossPad = 16; + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + glBegin(GL_QUADS); + + glColor3f(0.0f, 0.0f, 1.0f); + + //Horizontal crosshair + glVertex2i(mouseX, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); + glVertex2i(mouseX, mouseY - pointerHeight + crossPad); + + //Vertical crosshair + glVertex2i(mouseX + crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); + glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + + glEnd(); } - const PalmData* palmData = NULL; - - if (palmIndex >= handData->getPalms().size()) { - return; - } - - if (handData->getPalms()[palmIndex].isActive()) { - palmData = &handData->getPalms()[palmIndex]; - } else { - return; - } - - // Get directon relative to avatar orientation - glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - - float cursorRange = glWidget->width(); - - int mouseX = cursorRange * xAngle; - int mouseY = cursorRange * yAngle; - - if (mouseX < 0) { - mouseX = 0; - } else if (mouseX > glWidget->width()) { - mouseX = glWidget->width(); - } - if (mouseY < 0) { - mouseY = 0; - } else if (mouseY > glWidget->width()) { - mouseY = glWidget->width(); - } - - const float pointerWidth = 40; - const float pointerHeight = 40; - const float crossPad = 16; - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - glBegin(GL_QUADS); - - glColor3f(0, 0, 1); - - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); - - glEnd(); } // Renders the overlays either to a texture or to the screen From db94ae7449adeb9f117295567681b724197c7ccf Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 10:27:54 -0700 Subject: [PATCH 47/99] Invert hydra move view and thrust controllers to match consoles --- examples/hydraMove.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index f211a450a3..675a885b6d 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -19,8 +19,8 @@ var damping = 0.9; var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }; var joysticksCaptured = false; -var THRUST_CONTROLLER = 1; -var VIEW_CONTROLLER = 0; +var THRUST_CONTROLLER = 0; +var VIEW_CONTROLLER = 1; var INITIAL_THRUST_MULTPLIER = 1.0; var THRUST_INCREASE_RATE = 1.05; var MAX_THRUST_MULTIPLIER = 75.0; From 762751ef6a35e19ed82f97aa1211209a1efd39e6 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 10:47:11 -0700 Subject: [PATCH 48/99] Store the last mouse move input type for application overlay --- interface/src/Application.cpp | 4 ++++ interface/src/Application.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 24fcb3dc70..9f9cdb698c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -150,6 +150,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _mouseX(0), _mouseY(0), _lastMouseMove(usecTimestampNow()), + _lastMouseMoveType(QEvent::MouseMove), _mouseHidden(false), _seenMouseMove(false), _touchAvgX(0.0f), @@ -1096,6 +1097,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) { showMouse = false; } + // Used by application overlay to determine how to draw cursor(s) + _lastMouseMoveType = event->type(); + _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it diff --git a/interface/src/Application.h b/interface/src/Application.h index 2889dcb301..536a54e65d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -206,6 +206,7 @@ public: const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } int getMouseX() const { return _mouseX; } int getMouseY() const { return _mouseY; } + unsigned int getLastMouseMoveType() const { return _lastMouseMoveType; } Faceplus* getFaceplus() { return &_faceplus; } Faceshift* getFaceshift() { return &_faceshift; } Visage* getVisage() { return &_visage; } @@ -505,6 +506,7 @@ private: int _mouseDragStartedX; int _mouseDragStartedY; quint64 _lastMouseMove; + unsigned int _lastMouseMoveType; bool _mouseHidden; bool _seenMouseMove; @@ -521,6 +523,7 @@ private: QSet _keysPressed; + GeometryCache _geometryCache; AnimationCache _animationCache; TextureCache _textureCache; From 18543ff3130a0051ab0d23a83b95b1dbd976e53e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 12:07:25 -0700 Subject: [PATCH 49/99] Support for multiple oculus overlay magnifiers --- interface/src/ui/ApplicationOverlay.cpp | 256 +++++++++++++----------- interface/src/ui/ApplicationOverlay.h | 4 + 2 files changed, 139 insertions(+), 121 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 1deab117ac..83b6dde93b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -16,6 +16,7 @@ #include "Application.h" #include "ApplicationOverlay.h" +#include "devices/OculusManager.h" #include "ui/Stats.h" @@ -35,7 +36,7 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -void renderControllerPointer() { +void ApplicationOverlay::renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); @@ -60,7 +61,7 @@ void renderControllerPointer() { if (handData->getPalms()[palmIndex].isActive()) { palmData = &handData->getPalms()[palmIndex]; } else { - return; + continue; } // Get directon relative to avatar orientation @@ -75,24 +76,25 @@ void renderControllerPointer() { int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; - if (mouseX < 0) { - //mouseX = 0; - continue; - } else if (mouseX > glWidget->width()) { - //mouseX = glWidget->width(); - continue; - } - if (mouseY < 0) { - //mouseY = 0; - continue; - } else if (mouseY > glWidget->width()) { - //mouseY = glWidget->width(); + if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { continue; } - const float pointerWidth = 40; - const float pointerHeight = 40; - const float crossPad = 16; + float pointerWidth = 40; + float pointerHeight = 40; + float crossPad = 16; + //if we have the oculus, we should make the cursor smaller since it will be + //magnified + if (OculusManager::isConnected()) { + pointerWidth /= 4; + pointerHeight /= 4; + crossPad /= 4; + + _mouseX[_numMagnifiers] = mouseX; + _mouseY[_numMagnifiers] = mouseY; + _numMagnifiers++; + + } mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; @@ -131,6 +133,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); + _numMagnifiers = 0; + int mouseX = application->getMouseX(); int mouseY = application->getMouseY(); bool renderPointer = renderToTexture; @@ -310,15 +314,21 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { overlays.render2D(); - // Render a crosshair over the pointer when in Oculus - if (renderPointer) { + // Render a crosshair over the mouse when in Oculus + if (renderPointer && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 10; const float pointerHeight = 10; const float crossPad = 4; + + _numMagnifiers = 1; + _mouseX[0] = application->getMouseX(); + _mouseY[0] = application->getMouseY(); + mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; + glBegin(GL_QUADS); glColor3f(1, 0, 0); @@ -336,7 +346,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(mouseX + crossPad, mouseY - pointerHeight); glEnd(); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } @@ -418,12 +428,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); const int widgetWidth = glWidget->width(); const int widgetHeight = glWidget->height(); - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; const float magnification = 4.0f; // Get vertical FoV of the displayed overlay texture @@ -480,114 +486,122 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.01f); - //Draw the magnifying glass - - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - - const float halfMagnifyHeight = magnifyHeight / 2.0f; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + //Draw the magnifiers + for (int i = 0; i < _numMagnifiers; i++) { - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * textureFov); - float rX = sin((newURight - 0.5f) * textureFov); - float bY = sin((newVBottom - 0.5f) * textureFov); - float tY = sin((newVTop - 0.5f) * textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; - glBegin(GL_QUADS); + int mouseX = _mouseX[i]; + int mouseY = _mouseY[i]; + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; + //clamp the magnification + if (mouseX < 0) { + magnifyWidth += mouseX; + mouseX = 0; + } else if (mouseX + magnifyWidth > widgetWidth) { + magnifyWidth = widgetWidth - mouseX; + } + if (mouseY < 0) { + magnifyHeight += mouseY; + mouseY = 0; + } else if (mouseY + magnifyHeight > widgetHeight) { + magnifyHeight = widgetHeight - mouseY; } - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; + const float halfMagnifyHeight = magnifyHeight / 2.0f; - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - glEnd(); + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get angle on the UI + float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + + float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * textureFov); + float rX = sin((newURight - 0.5f) * textureFov); + float bY = sin((newVBottom - 0.5f) * textureFov); + float tY = sin((newVTop - 0.5f) * textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + + } else { + leftX = sin(leftAngle) * _distance; + rightX = sin(rightAngle) * _distance; + leftZ = -cos(leftAngle) * _distance; + rightZ = -cos(rightAngle) * _distance; + if (_uiType == CURVED_SEMICIRCLE) { + topZ = -cos(topAngle * overlayAspectRatio) * _distance; + bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; + } else { + // Dont want to use topZ or bottomZ for SEMICIRCLE + topZ = -99999; + bottomZ = -99999; + } + + float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; + float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; + + //TODO: Remove immediate mode in favor of VBO + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + + glEnd(); + } } + glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 1bf0e18816..6dbcaa7afb 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -46,6 +46,7 @@ private: typedef QPair VerticesIndices; + void renderControllerPointer(); void renderTexturedHemisphere(); QOpenGLFramebufferObject* _framebufferObject; @@ -53,6 +54,9 @@ private: float _oculusAngle; float _distance; UIType _uiType; + int _mouseX[2]; + int _mouseY[2]; + int _numMagnifiers; }; #endif // hifi_ApplicationOverlay_h \ No newline at end of file From bc293071d6193d68734c0cc5a152046caae76d44 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 12:25:22 -0700 Subject: [PATCH 50/99] Removed left handed menu option --- interface/src/Menu.cpp | 1 - interface/src/Menu.h | 1 - interface/src/devices/SixenseManager.cpp | 3 --- interface/src/ui/ApplicationOverlay.cpp | 10 ++-------- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6cf99418e3..0c6a3efa24 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -382,7 +382,6 @@ Menu::Menu() : QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLeftHanded, 0, false); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 8415fb68c4..47ada75287 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -400,7 +400,6 @@ namespace MenuOption { const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; - const QString SixenseLeftHanded = "Left Handed Sixense Mouse Input"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 37bad28093..bce55ae362 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -118,10 +118,7 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - // Check if we are on the correct palm - //if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { emulateMouse(palm, numActiveControllers - 1); - //} } // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 83b6dde93b..873f7c29f3 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -44,14 +44,7 @@ void ApplicationOverlay::renderControllerPointer() { const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); int numberOfPalms = handData->getNumPalms(); - - int palmIndex; - /*if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { - palmIndex = 2; - } else { - palmIndex = 3; - }*/ - for (palmIndex = 2; palmIndex < 4; palmIndex++) { + for (int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -76,6 +69,7 @@ void ApplicationOverlay::renderControllerPointer() { int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; + //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { continue; } From d689c6b39fe31753cd4b5b5519d34cd566516e63 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Jun 2014 13:15:54 -0700 Subject: [PATCH 51/99] fix reference to attribute in STUN packet --- libraries/networking/src/NodeList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 9a298ce26c..b872ec12cf 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -281,7 +281,7 @@ void NodeList::processSTUNResponse(const QByteArray& packet) { int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN; uint8_t addressFamily = 0; - memcpy(&addressFamily, packet.data(), sizeof(addressFamily)); + memcpy(&addressFamily, packet.data() + byteIndex, sizeof(addressFamily)); byteIndex += sizeof(addressFamily); From a13ef5c3bf4e4dc82cb49f66e32592de3c7f2a0b Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 13:19:02 -0700 Subject: [PATCH 52/99] Made applicationoverlay more readable --- interface/src/ui/ApplicationOverlay.cpp | 812 +++++++++++++----------- interface/src/ui/ApplicationOverlay.h | 5 + 2 files changed, 439 insertions(+), 378 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 873f7c29f3..9af3ed6d02 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -20,10 +20,20 @@ #include "ui/Stats.h" +// Fast helper functions +inline float max(float a, float b) { + return (a > b) ? a : b; +} + +inline float min(float a, float b) { + return (a < b) ? a : b; +} + ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), + _textureFov(PI / 2.5f), _uiType(HEMISPHERE) { } @@ -36,6 +46,273 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +// Renders the overlays either to a texture or to the screen +void ApplicationOverlay::renderOverlay(bool renderToTexture) { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + + Application* application = Application::getInstance(); + + Overlays& overlays = application->getOverlays(); + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + + if (renderToTexture) { + getFramebufferObject()->bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Render 2D overlay + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + glLoadIdentity(); + gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + renderAudioMeter(); + + if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { + myAvatar->renderHeadMouse(glWidget->width(), glWidget->height()); + } + + renderStatsAndLogs(); + + // give external parties a change to hook in + emit application->renderingOverlay(); + + overlays.render2D(); + + renderPointers(); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + + if (renderToTexture) { + getFramebufferObject()->release(); + } +} + +// Draws the FBO texture for the screen +void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { + + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + glLoadIdentity(); + gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2i(0, glWidget->height()); + glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height()); + glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0); + glTexCoord2f(0, 1); glVertex2i(0, 0); + glEnd(); + + glPopMatrix(); + glDisable(GL_TEXTURE_2D); +} + +void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const { + glm::quat rot = Application::getInstance()->getAvatar()->getOrientation(); + + //invert y direction + y = 1.0 - y; + + //Get position on hemisphere UI + x = sin((x - 0.5f) * _textureFov); + y = sin((y - 0.5f) * _textureFov); + + float dist = sqrt(x * x + y * y); + float z = -sqrt(1.0f - dist * dist); + + //Rotate the UI pick ray by the avatar orientation + direction = glm::normalize(rot * glm::vec3(x, y, z)); +} + +// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. +void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { + + Application* application = Application::getInstance(); + + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + // Get vertical FoV of the displayed overlay texture + const float halfVerticalAngle = _oculusAngle / 2.0f; + const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); + const float halfOverlayHeight = _distance * tan(halfVerticalAngle); + const float overlayHeight = halfOverlayHeight * 2.0f; + + // The more vertices, the better the curve + const int numHorizontalVertices = 20; + const int numVerticalVertices = 20; + // U texture coordinate width at each quad + const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); + const float quadTexHeight = 1.0f / (numVerticalVertices - 1); + + // Get horizontal angle and angle increment from vertical angle and aspect ratio + const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; + const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); + const float halfHorizontalAngle = horizontalAngle / 2; + + const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); + + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); + glEnable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + + glMatrixMode(GL_MODELVIEW); + + glPushMatrix(); + glLoadIdentity(); + // Transform to world space + glm::quat rotation = whichCamera.getRotation(); + glm::vec3 axis2 = glm::axis(rotation); + glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); + glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); + + // Translate to the front of the camera + glm::vec3 pos = whichCamera.getPosition(); + glm::quat rot = myAvatar->getOrientation(); + glm::vec3 axis = glm::axis(rot); + + glTranslatef(pos.x, pos.y, pos.z); + glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); + + glColor3f(1.0f, 1.0f, 1.0f); + + glDepthMask(GL_TRUE); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.01f); + + float leftX, rightX, leftZ, rightZ, topZ, bottomZ; + + //Draw the magnifiers + for (int i = 0; i < _numMagnifiers; i++) { + renderMagnifier(_mouseX[i], _mouseY[i]); + } + + glDepthMask(GL_FALSE); + glDisable(GL_ALPHA_TEST); + + //TODO: Remove immediate mode in favor of VBO + if (_uiType == HEMISPHERE) { + renderTexturedHemisphere(); + } else{ + glBegin(GL_QUADS); + // Place the vertices in a semicircle curve around the camera + for (int i = 0; i < numHorizontalVertices - 1; i++) { + for (int j = 0; j < numVerticalVertices - 1; j++) { + + // Calculate the X and Z coordinates from the angles and radius from camera + leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; + rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; + leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; + rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; + if (_uiType == 2) { + topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; + bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; + } else { + topZ = -99999; + bottomZ = -99999; + } + + glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); + glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); + glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); + glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); + glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); + glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); + glTexCoord2f(quadTexWidth * i, j * quadTexHeight); + glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); + } + } + + glEnd(); + } + + glPopMatrix(); + + glDepthMask(GL_TRUE); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glEnable(GL_LIGHTING); + +} + +//Renders optional pointers +void ApplicationOverlay::renderPointers() { + Application* application = Application::getInstance(); + // Render a crosshair over the mouse when in Oculus + _numMagnifiers = 0; + int mouseX = application->getMouseX(); + int mouseY = application->getMouseY(); + + if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { + const float pointerWidth = 10; + const float pointerHeight = 10; + const float crossPad = 4; + + _numMagnifiers = 1; + _mouseX[0] = application->getMouseX(); + _mouseY[0] = application->getMouseY(); + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + + glBegin(GL_QUADS); + + glColor3f(1, 0, 0); + + //Horizontal crosshair + glVertex2i(mouseX, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); + glVertex2i(mouseX, mouseY - pointerHeight + crossPad); + + //Vertical crosshair + glVertex2i(mouseX + crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); + glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + + glEnd(); + } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + //only render controller pointer if we aren't already rendering a mouse pointer + renderControllerPointer(); + } +} + void ApplicationOverlay::renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); @@ -87,7 +364,7 @@ void ApplicationOverlay::renderControllerPointer() { _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; _numMagnifiers++; - + } mouseX -= pointerWidth / 2.0f; @@ -113,41 +390,154 @@ void ApplicationOverlay::renderControllerPointer() { } } -// Renders the overlays either to a texture or to the screen -void ApplicationOverlay::renderOverlay(bool renderToTexture) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); + + float leftX, rightX, leftZ, rightZ, topZ, bottomZ; + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + // Get vertical FoV of the displayed overlay texture + const float halfVerticalAngle = _oculusAngle / 2.0f; + const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); + const float halfOverlayHeight = _distance * tan(halfVerticalAngle); + const float overlayHeight = halfOverlayHeight * 2.0f; + + // The more vertices, the better the curve + const int numHorizontalVertices = 20; + const int numVerticalVertices = 20; + // U texture coordinate width at each quad + const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); + const float quadTexHeight = 1.0f / (numVerticalVertices - 1); + + // Get horizontal angle and angle increment from vertical angle and aspect ratio + const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; + const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); + const float halfHorizontalAngle = horizontalAngle / 2; + + + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; + + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; + + //clamp the magnification + if (mouseX < 0) { + magnifyWidth += mouseX; + mouseX = 0; + } else if (mouseX + magnifyWidth > widgetWidth) { + magnifyWidth = widgetWidth - mouseX; + } + if (mouseY < 0) { + magnifyHeight += mouseY; + mouseY = 0; + } else if (mouseY + magnifyHeight > widgetHeight) { + magnifyHeight = widgetHeight - mouseY; + } + + const float halfMagnifyHeight = magnifyHeight / 2.0f; + + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; + + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; + + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get angle on the UI + float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + + float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + + } else { + leftX = sin(leftAngle) * _distance; + rightX = sin(rightAngle) * _distance; + leftZ = -cos(leftAngle) * _distance; + rightZ = -cos(rightAngle) * _distance; + if (_uiType == CURVED_SEMICIRCLE) { + topZ = -cos(topAngle * overlayAspectRatio) * _distance; + bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; + } else { + // Dont want to use topZ or bottomZ for SEMICIRCLE + topZ = -99999; + bottomZ = -99999; + } + + float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; + float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; + + //TODO: Remove immediate mode in favor of VBO + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + + glEnd(); + } +} + +void ApplicationOverlay::renderAudioMeter() { Application* application = Application::getInstance(); - Overlays& overlays = application->getOverlays(); QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); Audio* audio = application->getAudio(); - const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); - BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); - NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); - - _numMagnifiers = 0; - - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); - bool renderPointer = renderToTexture; - - if (renderToTexture) { - getFramebufferObject()->bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // Render 2D overlay: I/O level bar graphs and text - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - - glLoadIdentity(); - gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); // Display a single screen-size quad to create an alpha blended 'collision' flash if (audio->getCollisionFlashesScreen()) { @@ -267,11 +657,16 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glEnd(); +} +void ApplicationOverlay::renderStatsAndLogs() { - if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { - myAvatar->renderHeadMouse(glWidget->width(), glWidget->height()); - } + Application* application = Application::getInstance(); + + QGLWidget* glWidget = application->getGLWidget(); + const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); + BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); + NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); // Display stats and log text onscreen glLineWidth(1.0f); @@ -302,357 +697,18 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { drawText(glWidget->width() - 100, glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT); } nodeBoundsDisplay.drawOverlay(); - - // give external parties a change to hook in - emit application->renderingOverlay(); - - overlays.render2D(); - - // Render a crosshair over the mouse when in Oculus - if (renderPointer && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 10; - const float pointerHeight = 10; - const float crossPad = 4; - - - _numMagnifiers = 1; - _mouseX[0] = application->getMouseX(); - _mouseY[0] = application->getMouseY(); - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - - glBegin(GL_QUADS); - - glColor3f(1, 0, 0); - - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); - - glEnd(); - } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - //only render controller pointer if we aren't already rendering a mouse pointer - renderControllerPointer(); - } - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - - if (renderToTexture) { - getFramebufferObject()->release(); - } -} - -// Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { - - Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); - - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - - glLoadIdentity(); - gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2i(0, glWidget->height()); - glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height()); - glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0); - glTexCoord2f(0, 1); glVertex2i(0, 0); - glEnd(); - - glPopMatrix(); - glDisable(GL_TEXTURE_2D); -} - -const float textureFov = PI / 2.5f; - -void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const { - glm::quat rot = Application::getInstance()->getAvatar()->getOrientation(); - - //invert y direction - y = 1.0 - y; - - //Get position on hemisphere UI - x = sin((x - 0.5f) * textureFov); - y = sin((y - 0.5f) * textureFov); - - float dist = sqrt(x * x + y * y); - float z = -sqrt(1.0f - dist * dist); - - //Rotate the UI pick ray by the avatar orientation - direction = glm::normalize(rot * glm::vec3(x, y, z)); -} - -// Fast helper functions -inline float max(float a, float b) { - return (a > b) ? a : b; -} - -inline float min(float a, float b) { - return (a < b) ? a : b; -} - -// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. -void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { - - Application* application = Application::getInstance(); - - QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); - const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; - - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); - const float halfHorizontalAngle = horizontalAngle / 2; - - const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); - - glActiveTexture(GL_TEXTURE0); - - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); - glEnable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - - glMatrixMode(GL_MODELVIEW); - - glPushMatrix(); - glLoadIdentity(); - // Transform to world space - glm::quat rotation = whichCamera.getRotation(); - glm::vec3 axis2 = glm::axis(rotation); - glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); - glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); - - // Translate to the front of the camera - glm::vec3 pos = whichCamera.getPosition(); - glm::quat rot = myAvatar->getOrientation(); - glm::vec3 axis = glm::axis(rot); - - glTranslatef(pos.x, pos.y, pos.z); - glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); - - glColor3f(1.0f, 1.0f, 1.0f); - - glDepthMask(GL_TRUE); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.01f); - - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - //Draw the magnifiers - for (int i = 0; i < _numMagnifiers; i++) { - - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; - - int mouseX = _mouseX[i]; - int mouseY = _mouseY[i]; - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - - const float halfMagnifyHeight = magnifyHeight / 2.0f; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - - //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; - - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * textureFov); - float rX = sin((newURight - 0.5f) * textureFov); - float bY = sin((newVBottom - 0.5f) * textureFov); - float tY = sin((newVTop - 0.5f) * textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); - - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; - } - - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; - - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); - - glEnd(); - } - } - - glDepthMask(GL_FALSE); - glDisable(GL_ALPHA_TEST); - - //TODO: Remove immediate mode in favor of VBO - if (_uiType == HEMISPHERE) { - renderTexturedHemisphere(); - } else{ - glBegin(GL_QUADS); - // Place the vertices in a semicircle curve around the camera - for (int i = 0; i < numHorizontalVertices - 1; i++) { - for (int j = 0; j < numVerticalVertices - 1; j++) { - - // Calculate the X and Z coordinates from the angles and radius from camera - leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; - rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; - rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - if (_uiType == 2) { - topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; - bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; - } else { - topZ = -99999; - bottomZ = -99999; - } - - glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); - glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); - glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); - glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); - glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); - glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); - glTexCoord2f(quadTexWidth * i, j * quadTexHeight); - glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); - } - } - - glEnd(); - } - - glPopMatrix(); - - glDepthMask(GL_TRUE); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); - } +//Renders a hemisphere with texture coordinates. void ApplicationOverlay::renderTexturedHemisphere() { const int slices = 80; const int stacks = 80; + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm static VerticesIndices vbo(0, 0); int vertices = slices * (stacks - 1) + 1; int indices = slices * 2 * 3 * (stacks - 2) + slices * 3; + //We only generate the VBO once if (vbo.first == 0) { TextureVertex* vertexData = new TextureVertex[vertices]; TextureVertex* vertex = vertexData; @@ -666,8 +722,8 @@ void ApplicationOverlay::renderTexturedHemisphere() { vertex->position.x = sinf(theta) * radius; vertex->position.y = cosf(theta) * radius; vertex->position.z = z; - vertex->uv.x = asin(vertex->position.x) / (textureFov) + 0.5f; - vertex->uv.y = asin(vertex->position.y) / (textureFov) + 0.5f; + vertex->uv.x = asin(vertex->position.x) / (_textureFov) + 0.5f; + vertex->uv.y = asin(vertex->position.y) / (_textureFov) + 0.5f; vertex++; } } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 6dbcaa7afb..03a323cd5d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -46,13 +46,18 @@ private: typedef QPair VerticesIndices; + void renderPointers(); void renderControllerPointer(); + void renderMagnifier(int mouseX, int mouseY); + void renderAudioMeter(); + void renderStatsAndLogs(); void renderTexturedHemisphere(); QOpenGLFramebufferObject* _framebufferObject; float _trailingAudioLoudness; float _oculusAngle; float _distance; + float _textureFov; UIType _uiType; int _mouseX[2]; int _mouseY[2]; From 91f23fcf2d7ac3c918e63e838d5c8e22d591cdc9 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 15:39:40 -0700 Subject: [PATCH 53/99] Fix for click and drag with sixense --- interface/src/devices/SixenseManager.cpp | 12 +++++++++++- interface/src/ui/ApplicationOverlay.cpp | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index bce55ae362..4df08c6966 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -381,7 +381,17 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { if (pos.x() != _oldX[index] || pos.y() != _oldY[index]) { QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); - Application::getInstance()->mouseMoveEvent(&mouseEvent); + //Only send the mouse event if the opposite left button isnt held down. + //This is specifically for edit voxels + if (triggerButton == Qt::LeftButton) { + if (!_triggerPressed[(int)(!index)]) { + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + } else { + if (!_bumperPressed[(int)(!index)]) { + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + } } _oldX[index] = pos.x(); _oldY[index] = pos.y(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9af3ed6d02..ae6b49c560 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -289,7 +289,6 @@ void ApplicationOverlay::renderPointers() { mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; - glBegin(GL_QUADS); glColor3f(1, 0, 0); From 16f9b8c3ed0a836435f246ef146a9ebdd70b091c Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 15:49:00 -0700 Subject: [PATCH 54/99] Removed some unused variables. --- interface/src/devices/SixenseManager.cpp | 1 - interface/src/ui/ApplicationOverlay.cpp | 15 ++------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 4df08c6966..fa902be46f 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -353,7 +353,6 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float cursorRange = widget->width(); - pos.setX(cursorRange * xAngle); pos.setY(cursorRange * yAngle); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ae6b49c560..95ac803e37 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -154,9 +154,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; + // Get vertical FoV of the displayed overlay texture const float halfVerticalAngle = _oculusAngle / 2.0f; @@ -320,7 +318,7 @@ void ApplicationOverlay::renderControllerPointer() { const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); int numberOfPalms = handData->getNumPalms(); - for (int palmIndex = 2; palmIndex < 4; palmIndex++) { + for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -407,18 +405,9 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const float halfVerticalAngle = _oculusAngle / 2.0f; const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); // Get horizontal angle and angle increment from vertical angle and aspect ratio const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); const float halfHorizontalAngle = horizontalAngle / 2; From 3e50443ea157fe19c50a2dee61e818ab3bbd5891 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Jun 2014 16:03:18 -0700 Subject: [PATCH 55/99] let QUrl do its automatic percent encoding for address lookup --- interface/src/location/LocationManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index f80c331df4..5567c93e58 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -78,7 +78,6 @@ void LocationManager::goTo(QString destination) { _userData = QJsonObject(); _placeData = QJsonObject(); - destination = QString(QUrl::toPercentEncoding(destination)); JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "goToUserFromResponse"; From b4bd774789a310a6570ee025e4f454542775e47b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 12 Jun 2014 16:22:32 -0700 Subject: [PATCH 56/99] Midpoint on generic object streaming. --- libraries/metavoxels/src/Bitstream.cpp | 177 +++++++++++++++++++++++-- libraries/metavoxels/src/Bitstream.h | 138 +++++++++++++++---- 2 files changed, 274 insertions(+), 41 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d285e4aa3f..f1c927451b 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1639,6 +1639,153 @@ QHash Bitstream::createPropertyWriters return propertyWriters; } +MappedObjectStreamer::MappedObjectStreamer(const QVector& properties) : + _properties(properties) { +} + +const char* MappedObjectStreamer::getName() const { + return _metaObject->className(); +} + +void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _properties.size(); + if (_properties.isEmpty()) { + return; + } + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const StreamerPropertyPair& property, _properties) { + out << property.first.data(); + if (full) { + out << QByteArray::fromRawData(property.second.name(), strlen(property.second.name())); + } else { + hash.addData(property.second.name(), strlen(property.second.name()) + 1); + } + } + if (!full) { + QByteArray hashResult = hash.result(); + out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } +} + +void MappedObjectStreamer::write(Bitstream& out, QObject* object) const { + foreach (const StreamerPropertyPair& property, _properties) { + property.first->write(out, property.second.read(object)); + } +} + +void MappedObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { + foreach (const StreamerPropertyPair& property, _properties) { + property.first->writeDelta(out, property.second.read(object), (reference && reference->metaObject() == _metaObject) ? + property.second.read(reference) : QVariant()); + } +} + +QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { + if (!object && _metaObject) { + object = _metaObject->newInstance(); + } + foreach (const StreamerPropertyPair& property, _properties) { + QVariant value = property.first->read(in); + if (property.second.isValid() && object) { + property.second.write(object, value); + } + } + return object; +} + +QObject* MappedObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { + if (!object && _metaObject) { + object = _metaObject->newInstance(); + } + foreach (const StreamerPropertyPair& property, _properties) { + QVariant value; + property.first->readDelta(in, value, (property.second.isValid() && reference) ? + property.second.read(reference) : QVariant()); + if (property.second.isValid() && object) { + property.second.write(object, value); + } + } + return object; +} + +GenericObjectStreamer::GenericObjectStreamer(const QByteArray& name, const QVector& properties, + const QByteArray& hash) : + _name(name), + _properties(properties), + _hash(hash) { + + _metaObject = &GenericSharedObject::staticMetaObject; +} + +const char* GenericObjectStreamer::getName() const { + return _name.constData(); +} + +void GenericObjectStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _properties.size(); + if (_properties.isEmpty()) { + return; + } + foreach (const StreamerNamePair& property, _properties) { + out << property.first.data(); + if (full) { + out << property.second; + } + } + if (!full) { + if (_hash.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const StreamerNamePair& property, _properties) { + hash.addData(property.second.constData(), property.second.size() + 1); + } + const_cast(this)->_hash = hash.result(); + } + out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); + } +} + +void GenericObjectStreamer::write(Bitstream& out, QObject* object) const { + const QVariantList& values = static_cast(object)->getValues(); + for (int i = 0; i < _properties.size(); i++) { + _properties.at(i).first->write(out, values.at(i)); + } +} + +void GenericObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { + for (int i = 0; i < _properties.size(); i++) { + _properties.at(i).first->writeDelta(out, static_cast(object)->getValues().at(i), reference ? + static_cast(reference)->getValues().at(i) : QVariant()); + } +} + +QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { + if (!object) { + object = new GenericSharedObject(_weakSelf); + } + QVariantList values; + foreach (const StreamerNamePair& property, _properties) { + values.append(property.first->read(in)); + } + static_cast(object)->setValues(values); + return object; +} + +QObject* GenericObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { + if (!object) { + object = new GenericSharedObject(_weakSelf); + } + QVariantList values; + for (int i = 0; i < _properties.size(); i++) { + const StreamerNamePair& property = _properties.at(i); + QVariant value; + property.first->readDelta(in, value, reference ? + static_cast(reference)->getValues().at(i) : QVariant()); + values.append(value); + } + static_cast(object)->setValues(values); + return object; +} + ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const PropertyReaderVector& properties) : _className(className), @@ -1713,6 +1860,19 @@ MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : _streamer(streamer) { } +GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : + _streamer(streamer), + _value(value) { +} + +bool GenericValue::operator==(const GenericValue& other) const { + return _streamer == other._streamer && _value == other._value; +} + +GenericSharedObject::GenericSharedObject(const ObjectStreamerPointer& streamer) : + _streamer(streamer) { +} + TypeStreamer::~TypeStreamer() { } @@ -2069,19 +2229,6 @@ TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { return ENUM_CATEGORY; } -GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : - _streamer(streamer), - _value(value) { -} - -bool GenericValue::operator==(const GenericValue& other) const { - return _streamer == other._streamer && _value == other._value; -} - -const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { - return value.value().getStreamer().data(); -} - MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, const QVector& fields) : _baseStreamer(baseStreamer), @@ -2334,3 +2481,7 @@ QVariant GenericMapTypeStreamer::read(Bitstream& in) const { TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { return MAP_CATEGORY; } + +const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { + return value.value().getStreamer().data(); +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index ee9b8b472c..07e4b94568 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -37,6 +37,7 @@ class AttributeValue; class Bitstream; class GenericValue; class ObjectReader; +class ObjectStreamer; class OwnedAttributeValue; class PropertyReader; class PropertyWriter; @@ -46,6 +47,8 @@ typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; typedef QPair NameIntPair; +typedef QSharedPointer ObjectStreamerPointer; +typedef QWeakPointer WeakObjectStreamerPointer; typedef QSharedPointer TypeStreamerPointer; typedef QWeakPointer WeakTypeStreamerPointer; typedef QVector PropertyReaderVector; @@ -766,6 +769,68 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +/// Contains the information required to stream an object. +class ObjectStreamer { +public: + + virtual const char* getName() const = 0; + virtual void writeMetadata(Bitstream& out, bool full) const = 0; + virtual void write(Bitstream& out, QObject* object) const = 0; + virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const = 0; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0; + virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; + +protected: + + const QMetaObject* _metaObject; + ObjectStreamerPointer _self; +}; + +typedef QPair StreamerPropertyPair; + +/// A streamer that maps to a local class. +class MappedObjectStreamer : public ObjectStreamer { +public: + + MappedObjectStreamer(const QVector& properties); + + virtual const char* getName() const; + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, QObject* object) const; + virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const; + virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + +private: + + QVector _properties; +}; + +typedef QPair StreamerNamePair; + +/// A streamer for generic objects. +class GenericObjectStreamer : public ObjectStreamer { +public: + + GenericObjectStreamer(const QByteArray& name, const QVector& properties, const QByteArray& hash); + + virtual const char* getName() const; + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, QObject* object) const; + virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const; + virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + +private: + + friend class Bitstream; + + QByteArray _name; + WeakObjectStreamerPointer _weakSelf; + QVector _properties; + QByteArray _hash; +}; + /// Contains the information required to read an object from the stream. class ObjectReader { public: @@ -849,6 +914,44 @@ Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); +/// Contains a value along with a pointer to its streamer. +class GenericValue { +public: + + GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); + + const TypeStreamerPointer& getStreamer() const { return _streamer; } + const QVariant& getValue() const { return _value; } + + bool operator==(const GenericValue& other) const; + +private: + + TypeStreamerPointer _streamer; + QVariant _value; +}; + +Q_DECLARE_METATYPE(GenericValue) + +/// Contains a list of property values along with a pointer to their metadata. +class GenericSharedObject : public SharedObject { + Q_OBJECT + +public: + + GenericSharedObject(const ObjectStreamerPointer& streamer); + + const ObjectStreamerPointer& getStreamer() const { return _streamer; } + + void setValues(const QVariantList& values) { _values = values; } + const QVariantList& getValues() const { return _values; } + +private: + + ObjectStreamerPointer _streamer; + QVariantList _values; +}; + /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: @@ -1012,32 +1115,6 @@ private: QByteArray _hash; }; -/// Contains a value along with a pointer to its streamer. -class GenericValue { -public: - - GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); - - const TypeStreamerPointer& getStreamer() const { return _streamer; } - const QVariant& getValue() const { return _value; } - - bool operator==(const GenericValue& other) const; - -private: - - TypeStreamerPointer _streamer; - QVariant _value; -}; - -Q_DECLARE_METATYPE(GenericValue) - -/// A streamer class for generic values. -class GenericValueStreamer : public SimpleTypeStreamer { -public: - - virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; -}; - /// A streamer for types compiled by mtc. template class StreamableTypeStreamer : public SimpleTypeStreamer { public: @@ -1068,8 +1145,6 @@ private: QVector _fields; }; -typedef QPair StreamerNamePair; - /// A streamer for generic enums. class GenericStreamableTypeStreamer : public GenericTypeStreamer { public: @@ -1238,6 +1313,13 @@ private: TypeStreamerPointer _valueStreamer; }; +/// A streamer class for generic values. +class GenericValueStreamer : public SimpleTypeStreamer { +public: + + virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); From 8e74398ed7fa4f04f7bbb954ee8aca06b0f430c4 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 17:06:59 -0700 Subject: [PATCH 57/99] Fixed command line args parsing bug --- libraries/shared/src/HifiConfigVariantMap.cpp | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index e8ab59ce2d..7975059736 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -19,71 +19,70 @@ #include "HifiConfigVariantMap.h" QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) { - + QVariantMap mergedMap; - + // Add anything in the CL parameter list to the variant map. // Take anything with a dash in it as a key, and the values after it as the value. - + const QString DASHED_KEY_REGEX_STRING = "(^-{1,2})([\\w-]+)"; QRegExp dashedKeyRegex(DASHED_KEY_REGEX_STRING); - + int keyIndex = argumentList.indexOf(dashedKeyRegex); int nextKeyIndex = 0; - + // check if there is a config file to read where we can pull config info not passed on command line const QString CONFIG_FILE_OPTION = "--config"; - + while (keyIndex != -1) { if (argumentList[keyIndex] != CONFIG_FILE_OPTION) { // we have a key - look forward to see how many values associate to it QString key = dashedKeyRegex.cap(2); - + nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1); - + if (nextKeyIndex == keyIndex + 1 || keyIndex == argumentList.size() - 1) { - // there's no value associated with this option, it's a boolean - // so add it to the variant map with NULL as value - mergedMap.insertMulti(key, QVariant()); + // this option is simply a switch, so add it to the map with a value of `true` + mergedMap.insertMulti(key, QVariant(true)); } else { - int maxIndex = (nextKeyIndex == -1) ? argumentList.size() - 1: nextKeyIndex; - + int maxIndex = (nextKeyIndex == -1) ? argumentList.size(): nextKeyIndex; + // there's at least one value associated with the option // pull the first value to start QString value = argumentList[keyIndex + 1]; - + // for any extra values, append them, with a space, to the value string - for (int i = keyIndex + 2; i <= maxIndex; i++) { + for (int i = keyIndex + 2; i < maxIndex; i++) { value += " " + argumentList[i]; } - + // add the finalized value to the merged map mergedMap.insert(key, value); } - + keyIndex = nextKeyIndex; } else { keyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1); } } - + int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION); - + if (configIndex != -1) { // we have a config file - try and read it QString configFilePath = argumentList[configIndex + 1]; QFile configFile(configFilePath); - + if (configFile.exists()) { qDebug() << "Reading JSON config file at" << configFilePath; configFile.open(QIODevice::ReadOnly); - + QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll()); QJsonObject rootObject = configDocument.object(); - + // enumerate the keys of the configDocument object foreach(const QString& key, rootObject.keys()) { - + if (!mergedMap.contains(key)) { // no match in existing list, add it mergedMap.insert(key, QVariant(rootObject[key])); @@ -93,6 +92,6 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL qDebug() << "Could not find JSON config file at" << configFilePath; } } - + return mergedMap; } From 47a88a2713dd98fa51265a43761bf98a6875a917 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 17:08:23 -0700 Subject: [PATCH 58/99] Updated AssignmentClient to use HifiConfigVariantMap... ...and also updated DomainServer to appropriately use boolean options --- assignment-client/src/AssignmentClient.cpp | 117 ++--- domain-server/src/DomainServer.cpp | 573 +++++++++++---------- 2 files changed, 342 insertions(+), 348 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 009bd42e88..da1feda7a0 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -41,73 +42,65 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : setOrganizationDomain("highfidelity.io"); setApplicationName("assignment-client"); QSettings::setDefaultFormat(QSettings::IniFormat); - - QStringList argumentList = arguments(); - - // register meta type is required for queued invoke method on Assignment subclasses - + // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - - const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t"; - int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION); - + + const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); + + const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t"; + const QString ASSIGNMENT_POOL_OPTION = "pool"; + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet"; + const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a"; + Assignment::Type requestAssignmentType = Assignment::AllTypes; - - if (argumentIndex != -1) { - requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt(); + + // check for an assignment type passed on the command line or in the config + if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) { + requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt(); } - - const QString ASSIGNMENT_POOL_OPTION = "--pool"; - - argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION); - + QString assignmentPool; - - if (argumentIndex != -1) { - assignmentPool = argumentList[argumentIndex + 1]; + + // check for an assignment pool passed on the command line or in the config + if (argumentVariantMap.contains(ASSIGNMENT_POOL_OPTION)) { + assignmentPool = argumentVariantMap.value(ASSIGNMENT_POOL_OPTION).toString(); } - + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); - - // check if we were passed a wallet UUID on the command line + + // check for a wallet UUID on the command line or in the config // this would represent where the user running AC wants funds sent to - - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; - if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { - QUuid walletUUID = QString(argumentList[argumentIndex + 1]); + if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + QUuid walletUUID = argumentVariantMap.value(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION).toString(); qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } - + // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); - + // check for an overriden assignment server hostname - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; - - argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); - - if (argumentIndex != -1) { - _assignmentServerHostname = argumentList[argumentIndex + 1]; - + if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) { + _assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); + // set the custom assignment socket on our NodeList HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT); - + nodeList->setAssignmentServerSocket(customAssignmentSocket); } - + // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required qDebug() << "Waiting for assignment -" << _requestAssignment; - + QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); - + // connect our readPendingDatagrams method to the readyRead() signal of the socket connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); - + // connections to AccountManager for authentication connect(&AccountManager::getInstance(), &AccountManager::authRequired, this, &AssignmentClient::handleAuthenticationRequest); @@ -121,49 +114,49 @@ void AssignmentClient::sendAssignmentRequest() { void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); - + QByteArray receivedPacket; HifiSockAddr senderSockAddr; - + while (nodeList->getNodeSocket().hasPendingDatagrams()) { receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); - + if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { // construct the deployed assignment from the packet data _currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket)); - + if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; - + // switch our DomainHandler hostname and port to whoever sent us the assignment - + nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname); nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID()); - + qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); - + // start the deployed assignment AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this); - + connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run); connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit); connect(_currentAssignment.data(), &ThreadedAssignment::finished, this, &AssignmentClient::assignmentCompleted); connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); - + _currentAssignment->moveToThread(workerThread); - + // move the NodeList to the thread used for the _current assignment nodeList->moveToThread(workerThread); - + // let the assignment handle the incoming datagrams for its duration disconnect(&nodeList->getNodeSocket(), 0, this, 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(), &ThreadedAssignment::readPendingDatagrams); - + // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { @@ -180,15 +173,15 @@ void AssignmentClient::readPendingDatagrams() { void AssignmentClient::handleAuthenticationRequest() { const QString DATA_SERVER_USERNAME_ENV = "HIFI_AC_USERNAME"; const QString DATA_SERVER_PASSWORD_ENV = "HIFI_AC_PASSWORD"; - + // this node will be using an authentication server, let's make sure we have a username/password QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment(); - + QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV); QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV); - + AccountManager& accountManager = AccountManager::getInstance(); - + if (!username.isEmpty() && !password.isEmpty()) { // ask the account manager to log us in from the env variables accountManager.requestAccessToken(username, password); @@ -196,7 +189,7 @@ void AssignmentClient::handleAuthenticationRequest() { qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString()) << "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV) << "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate."; - + return; } } @@ -204,15 +197,15 @@ void AssignmentClient::handleAuthenticationRequest() { void AssignmentClient::assignmentCompleted() { // reset the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - + qDebug("Assignment finished or never started - waiting for new assignment."); - + NodeList* nodeList = NodeList::getInstance(); // have us handle incoming NodeList datagrams again disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); - + // clear our current assignment shared pointer now that we're done with it // if the assignment thread is still around it has its own shared pointer to the assignment _currentAssignment.clear(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b5c2d00d3e..f7185063a9 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -47,15 +47,15 @@ DomainServer::DomainServer(int argc, char* argv[]) : setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); QSettings::setDefaultFormat(QSettings::IniFormat); - + _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - + _networkAccessManager = new QNetworkAccessManager(this); - + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { // we either read a certificate and private key or were not passed one // and completed login or did not need to - + qDebug() << "Setting up LimitedNodeList and assignments."; setupNodeListAndAssignments(); } @@ -65,58 +65,58 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() { const QString X509_CERTIFICATE_OPTION = "cert"; const QString X509_PRIVATE_KEY_OPTION = "key"; const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE"; - + QString certPath = _argumentVariantMap.value(X509_CERTIFICATE_OPTION).toString(); QString keyPath = _argumentVariantMap.value(X509_PRIVATE_KEY_OPTION).toString(); - + if (!certPath.isEmpty() && !keyPath.isEmpty()) { // the user wants to use DTLS to encrypt communication with nodes // let's make sure we can load the key and certificate // _x509Credentials = new gnutls_certificate_credentials_t; // gnutls_certificate_allocate_credentials(_x509Credentials); - + QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV); - + qDebug() << "Reading certificate file at" << certPath << "for DTLS."; qDebug() << "Reading key file at" << keyPath << "for DTLS."; - + // int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials, // certPath.toLocal8Bit().constData(), // keyPath.toLocal8Bit().constData(), // GNUTLS_X509_FMT_PEM, // keyPassphraseString.toLocal8Bit().constData(), // 0); -// +// // if (gnutlsReturn < 0) { // qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit."; // QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); // return false; // } - + // qDebug() << "Successfully read certificate and private key."; - + // we need to also pass this certificate and private key to the HTTPS manager // this is used for Oauth callbacks when authorizing users against a data server - + QFile certFile(certPath); certFile.open(QIODevice::ReadOnly); - + QFile keyFile(keyPath); keyFile.open(QIODevice::ReadOnly); - + QSslCertificate sslCertificate(&certFile); QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8()); - + _httpsManager = new HTTPSManager(DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this, this); - + qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT; - + } else if (!certPath.isEmpty() || !keyPath.isEmpty()) { qDebug() << "Missing certificate or private key. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); return false; } - + return true; } @@ -125,12 +125,12 @@ bool DomainServer::optionallySetupOAuth() { const QString OAUTH_CLIENT_ID_OPTION = "oauth-client-id"; const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET"; const QString REDIRECT_HOSTNAME_OPTION = "hostname"; - + _oauthProviderURL = QUrl(_argumentVariantMap.value(OAUTH_PROVIDER_URL_OPTION).toString()); _oauthClientID = _argumentVariantMap.value(OAUTH_CLIENT_ID_OPTION).toString(); _oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV); _hostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString(); - + if (!_oauthClientID.isEmpty()) { if (_oauthProviderURL.isEmpty() || _hostname.isEmpty() @@ -144,47 +144,47 @@ bool DomainServer::optionallySetupOAuth() { qDebug() << "OAuth Client ID is" << _oauthClientID; } } - + return true; } void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { - + const QString CUSTOM_PORT_OPTION = "port"; unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT; - + if (_argumentVariantMap.contains(CUSTOM_PORT_OPTION)) { domainServerPort = (unsigned short) _argumentVariantMap.value(CUSTOM_PORT_OPTION).toUInt(); } - + unsigned short domainServerDTLSPort = 0; - + if (_isUsingDTLS) { domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT; - + const QString CUSTOM_DTLS_PORT_OPTION = "dtls-port"; - + if (_argumentVariantMap.contains(CUSTOM_DTLS_PORT_OPTION)) { domainServerDTLSPort = (unsigned short) _argumentVariantMap.value(CUSTOM_DTLS_PORT_OPTION).toUInt(); } } - + QSet parsedTypes; parseAssignmentConfigs(parsedTypes); - + populateDefaultStaticAssignmentsExcludingTypes(parsedTypes); - + LimitedNodeList* nodeList = LimitedNodeList::createInstance(domainServerPort, domainServerDTLSPort); - + connect(nodeList, &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList, &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); - + QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams())); - + // add whatever static assignments that have been parsed to the queue addStaticAssignmentsToQueue(); } @@ -194,21 +194,22 @@ bool DomainServer::optionallySetupAssignmentPayment() { const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments"; const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD"; - - if (_argumentVariantMap.contains(PAY_FOR_ASSIGNMENTS_OPTION)) { + + if (_argumentVariantMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) && + _argumentVariantMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool()) { if (!_oauthProviderURL.isEmpty()) { - + AccountManager& accountManager = AccountManager::getInstance(); accountManager.setAuthURL(_oauthProviderURL); - + if (!accountManager.hasValidAccessToken()) { // we don't have a valid access token so we need to get one QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY); QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY); - + if (!username.isEmpty() && !password.isEmpty()) { accountManager.requestAccessToken(username, password); - + // connect to loginFailed signal from AccountManager so we can quit if that is the case connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed); } else { @@ -217,31 +218,31 @@ bool DomainServer::optionallySetupAssignmentPayment() { return false; } } - + qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString()); - + // assume that the fact we are authing against HF data server means we will pay for assignments // setup a timer to send transactions to pay assigned nodes every 30 seconds QTimer* creditSetupTimer = new QTimer(this); connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); - + const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); - + QTimer* nodePaymentTimer = new QTimer(this); connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); - + const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); - + } else { qDebug() << "Missing OAuth provider URL, but assigned node payment was enabled. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); - + return false; } } - + return true; } @@ -254,35 +255,35 @@ void DomainServer::parseAssignmentConfigs(QSet& excludedTypes) // check for configs from the command line, these take precedence const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)"; QRegExp assignmentConfigRegex(ASSIGNMENT_CONFIG_REGEX_STRING); - + // scan for assignment config keys QStringList variantMapKeys = _argumentVariantMap.keys(); int configIndex = variantMapKeys.indexOf(assignmentConfigRegex); - + while (configIndex != -1) { // figure out which assignment type this matches Assignment::Type assignmentType = (Assignment::Type) assignmentConfigRegex.cap(1).toInt(); - + if (assignmentType < Assignment::AllTypes && !excludedTypes.contains(assignmentType)) { QVariant mapValue = _argumentVariantMap[variantMapKeys[configIndex]]; QJsonArray assignmentArray; - + if (mapValue.type() == QVariant::String) { QJsonDocument deserializedDocument = QJsonDocument::fromJson(mapValue.toString().toUtf8()); assignmentArray = deserializedDocument.array(); } else { assignmentArray = mapValue.toJsonValue().toArray(); } - + if (assignmentType != Assignment::AgentType) { createStaticAssignmentsForType(assignmentType, assignmentArray); } else { createScriptedAssignmentsFromArray(assignmentArray); } - + excludedTypes.insert(assignmentType); } - + configIndex = variantMapKeys.indexOf(assignmentConfigRegex, configIndex + 1); } } @@ -293,34 +294,34 @@ void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment _allAssignments.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment)); } -void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) { +void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) { foreach(const QJsonValue& jsonValue, configArray) { if (jsonValue.isObject()) { QJsonObject jsonObject = jsonValue.toObject(); - + // make sure we were passed a URL, otherwise this is an invalid scripted assignment const QString ASSIGNMENT_URL_KEY = "url"; QString assignmentURL = jsonObject[ASSIGNMENT_URL_KEY].toString(); - + if (!assignmentURL.isEmpty()) { // check the json for a pool const QString ASSIGNMENT_POOL_KEY = "pool"; QString assignmentPool = jsonObject[ASSIGNMENT_POOL_KEY].toString(); - + // check for a number of instances, if not passed then default is 1 const QString ASSIGNMENT_INSTANCES_KEY = "instances"; int numInstances = jsonObject[ASSIGNMENT_INSTANCES_KEY].toInt(); numInstances = (numInstances == 0 ? 1 : numInstances); - + qDebug() << "Adding a static scripted assignment from" << assignmentURL; - + for (int i = 0; i < numInstances; i++) { // add a scripted assignment to the queue for this instance Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, assignmentPool); scriptAssignment->setPayload(assignmentURL.toUtf8()); - + // scripts passed on CL or via JSON are static - so they are added back to the queue if the node dies addStaticAssignmentToAssignmentHash(scriptAssignment); } @@ -332,38 +333,38 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const QJsonArray& configArray) { // we have a string for config for this type qDebug() << "Parsing config for assignment type" << type; - + int configCounter = 0; - + foreach(const QJsonValue& jsonValue, configArray) { if (jsonValue.isObject()) { QJsonObject jsonObject = jsonValue.toObject(); - + // check the config string for a pool const QString ASSIGNMENT_POOL_KEY = "pool"; QString assignmentPool; - + QJsonValue poolValue = jsonObject[ASSIGNMENT_POOL_KEY]; if (!poolValue.isUndefined()) { assignmentPool = poolValue.toString(); - + jsonObject.remove(ASSIGNMENT_POOL_KEY); } - + ++configCounter; qDebug() << "Type" << type << "config" << configCounter << "=" << jsonObject; - + Assignment* configAssignment = new Assignment(Assignment::CreateCommand, type, assignmentPool); - + // setup the payload as a semi-colon separated list of key = value QStringList payloadStringList; foreach(const QString& payloadKey, jsonObject.keys()) { QString dashes = payloadKey.size() == 1 ? "-" : "--"; payloadStringList << QString("%1%2 %3").arg(dashes).arg(payloadKey).arg(jsonObject[payloadKey].toString()); } - + configAssignment->setPayload(payloadStringList.join(' ').toUtf8()); - + addStaticAssignmentToAssignmentHash(configAssignment); } } @@ -393,27 +394,27 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; - + int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr); - + QUuid packetUUID = uuidFromPacketHeader(packet); // check if this connect request matches an assignment in the queue bool isAssignment = _pendingAssignedNodes.contains(packetUUID); SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); PendingAssignedNodeData* pendingAssigneeData = NULL; - + if (isAssignment) { pendingAssigneeData = _pendingAssignedNodes.value(packetUUID); - + if (pendingAssigneeData) { matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType); - + if (matchingQueuedAssignment) { qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID) << "matches unfulfilled assignment" << uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID()); - + // remove this unique assignment deployment from the hash of pending assigned nodes // cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList _pendingAssignedNodes.remove(packetUUID); @@ -424,9 +425,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock return; } } - + } - + if (!isAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) { // this is an Agent, and we require authentication so we can compare the user's roles to our list of allowed ones if (_sessionAuthenticationHash.contains(packetUUID)) { @@ -445,37 +446,37 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append); QUrl authorizationURL = packetUUID.isNull() ? oauthAuthorizationURL() : oauthAuthorizationURL(packetUUID); oauthRequestStream << authorizationURL; - + // send this oauth request datagram back to the client LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); - + return; } } - + if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) || (isAssignment && matchingQueuedAssignment)) { // this was either not a static assignment or it was and we had a matching one in the queue - + // create a new session UUID for this node QUuid nodeUUID = QUuid::createUuid(); - + SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr); // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); - + if (isAssignment) { nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID()); - + // now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data delete pendingAssigneeData; } - + nodeData->setSendingSockAddr(senderSockAddr); - + // reply back to the user with a PacketTypeDomainList sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); } @@ -492,26 +493,26 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { // for now these are all interface clients that have a GUI // so just send them back the full authorization URL QUrl authorizationURL = _oauthProviderURL; - + const QString OAUTH_AUTHORIZATION_PATH = "/oauth/authorize"; authorizationURL.setPath(OAUTH_AUTHORIZATION_PATH); - + QUrlQuery authorizationQuery; - + authorizationQuery.addQueryItem(OAUTH_CLIENT_ID_QUERY_KEY, _oauthClientID); - + const QString OAUTH_RESPONSE_TYPE_QUERY_KEY = "response_type"; const QString OAUTH_REPSONSE_TYPE_QUERY_VALUE = "code"; authorizationQuery.addQueryItem(OAUTH_RESPONSE_TYPE_QUERY_KEY, OAUTH_REPSONSE_TYPE_QUERY_VALUE); - + const QString OAUTH_STATE_QUERY_KEY = "state"; // create a new UUID that will be the state parameter for oauth authorization AND the new session UUID for that node authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(stateUUID)); - + authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, oauthRedirectURL().toString()); - + authorizationURL.setQuery(authorizationQuery); - + return authorizationURL; } @@ -520,14 +521,14 @@ int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& const HifiSockAddr& senderSockAddr) { QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); - + packetStream >> nodeType; packetStream >> publicSockAddr >> localSockAddr; - + if (publicSockAddr.getAddress().isNull()) { // this node wants to use us its STUN server // so set the node public address to whatever we perceive the public address to be - + // if the sender is on our box then leave its public address to 0 so that // other users attempt to reach it on the same address they have for the domain-server if (senderSockAddr.getAddress().isLoopback()) { @@ -536,95 +537,95 @@ int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr.setAddress(senderSockAddr.getAddress()); } } - + return packetStream.device()->pos(); } NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) { QDataStream packetStream(packet); packetStream.skipRawData(numPreceedingBytes); - + quint8 numInterestTypes = 0; packetStream >> numInterestTypes; - + quint8 nodeType; NodeSet nodeInterestSet; - + for (int i = 0; i < numInterestTypes; i++) { packetStream >> nodeType; nodeInterestSet.insert((NodeType_t) nodeType); } - + return nodeInterestSet; } void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr, const NodeSet& nodeInterestList) { - + QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList); - + // always send the node their own UUID back QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append); broadcastDataStream << node->getUUID(); - + int numBroadcastPacketLeadBytes = broadcastDataStream.device()->pos(); - + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + LimitedNodeList* nodeList = LimitedNodeList::getInstance(); - + if (nodeInterestList.size() > 0) { - + // DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; int dataMTU = MAX_PACKET_SIZE; - + if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) { - + // reset our nodeByteArray and nodeDataStream QByteArray nodeByteArray; QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); - + if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) { - + // don't send avatar nodes to other avatars, that will come from avatar mixer nodeDataStream << *otherNode.data(); - + // pack the secret that these two nodes will use to communicate with each other QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID()); if (secretUUID.isNull()) { // generate a new secret UUID these two nodes can use secretUUID = QUuid::createUuid(); - + // set that on the current Node's sessionSecretHash nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID); - + // set it on the other Node's sessionSecretHash reinterpret_cast(otherNode->getLinkedData()) ->getSessionSecretHash().insert(node->getUUID(), secretUUID); - + } - + nodeDataStream << secretUUID; - + if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) { // we need to break here and start a new packet // so send the current one - + nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); - + // reset the broadcastPacket structure broadcastPacket.resize(numBroadcastPacketLeadBytes); broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes); } - + // append the nodeByteArray to the current state of broadcastDataStream broadcastPacket.append(nodeByteArray); } } } - + // always write the last broadcastPacket nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); } @@ -635,7 +636,7 @@ void DomainServer::readAvailableDatagrams() { HifiSockAddr senderSockAddr; QByteArray receivedPacket; - + static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); @@ -648,44 +649,44 @@ void DomainServer::readAvailableDatagrams() { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); - + // Suppress these for Assignment::AgentType to once per 5 seconds static QElapsedTimer noisyMessageTimer; static bool wasNoisyTimerStarted = false; - + if (!wasNoisyTimerStarted) { noisyMessageTimer.start(); wasNoisyTimerStarted = true; } - + const quint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; - + if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { qDebug() << "Received a request for assignment type" << requestAssignment.getType() << "from" << senderSockAddr; noisyMessageTimer.restart(); } - + SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); - + if (assignmentToDeploy) { qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr; - + // give this assignment out, either the type matches or the requestor said they will take any assignmentPacket.resize(numAssignmentPacketHeaderBytes); - + // setup a copy of this assignment that will have a unique UUID, for packaging purposes Assignment uniqueAssignment(*assignmentToDeploy.data()); uniqueAssignment.setUUID(QUuid::createUuid()); - + QDataStream assignmentStream(&assignmentPacket, QIODevice::Append); - + assignmentStream << uniqueAssignment; - + nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); - + // add the information for that deployed assignment to the hash of pending assigned nodes PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(), requestAssignment.getWalletUUID()); @@ -705,7 +706,7 @@ void DomainServer::readAvailableDatagrams() { // we're using DTLS, so tell the sender to get back to us using DTLS static QByteArray dtlsRequiredPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS); static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS); - + if (dtlsRequiredPacket.size() == numBytesDTLSHeader) { // pack the port that we accept DTLS traffic on unsigned short dtlsPort = nodeList->getDTLSSocket().localPort(); @@ -721,12 +722,12 @@ void DomainServer::setupPendingAssignmentCredits() { // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + if (!nodeData->getAssignmentUUID().isNull() && !nodeData->getWalletUUID().isNull()) { // check if we have a non-finalized transaction for this node to add this amount to TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); WalletTransaction* existingTransaction = NULL; - + while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { if (!i.value()->isFinalized()) { existingTransaction = i.value(); @@ -735,16 +736,16 @@ void DomainServer::setupPendingAssignmentCredits() { ++i; } } - + qint64 elapsedMsecsSinceLastPayment = nodeData->getPaymentIntervalTimer().elapsed(); nodeData->getPaymentIntervalTimer().restart(); - + const float CREDITS_PER_HOUR = 0.10f; const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000); const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * SATOSHIS_PER_CREDIT; - + float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC; - + if (existingTransaction) { existingTransaction->incrementAmount(pendingCredits); } else { @@ -757,30 +758,30 @@ void DomainServer::setupPendingAssignmentCredits() { } void DomainServer::sendPendingTransactionsToServer() { - + AccountManager& accountManager = AccountManager::getInstance(); - + if (accountManager.hasValidAccessToken()) { - + // enumerate the pending transactions and send them to the server to complete payment TransactionHash::iterator i = _pendingAssignmentCredits.begin(); - + JSONCallbackParameters transactionCallbackParams; - + transactionCallbackParams.jsonCallbackReceiver = this; transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback"; - + while (i != _pendingAssignmentCredits.end()) { accountManager.authenticatedRequest("api/v1/transactions", QNetworkAccessManager::PostOperation, transactionCallbackParams, i.value()->postJson().toJson()); - + // set this transaction to finalized so we don't add additional credits to it i.value()->setIsFinalized(true); - + ++i; } } - + } void DomainServer::transactionJSONCallback(const QJsonObject& data) { @@ -789,18 +790,18 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) { // create a dummy wallet transaction to unpack the JSON to WalletTransaction dummyTransaction; dummyTransaction.loadFromJson(data); - + TransactionHash::iterator i = _pendingAssignmentCredits.find(dummyTransaction.getDestinationUUID()); - + while (i != _pendingAssignmentCredits.end() && i.key() == dummyTransaction.getDestinationUUID()) { if (i.value()->getUUID() == dummyTransaction.getUUID()) { // we have a match - we can remove this from the hash of pending credits // and delete it for clean up - + WalletTransaction* matchingTransaction = i.value(); _pendingAssignmentCredits.erase(i); delete matchingTransaction; - + break; } else { ++i; @@ -811,28 +812,28 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) { void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { LimitedNodeList* nodeList = LimitedNodeList::getInstance(); - + if (nodeList->packetVersionAndHashMatch(receivedPacket)) { PacketType requestType = packetTypeForPacket(receivedPacket); - + if (requestType == PacketTypeDomainConnectRequest) { handleConnectRequest(receivedPacket, senderSockAddr); } else if (requestType == PacketTypeDomainListRequest) { QUuid nodeUUID = uuidFromPacketHeader(receivedPacket); - + if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) { NodeType_t throwawayNodeType; HifiSockAddr nodePublicAddress, nodeLocalAddress; - + int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, receivedPacket, senderSockAddr); - + SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress); - + // update last receive to now quint64 timeNow = usecTimestampNow(); checkInNode->setLastHeardMicrostamp(timeNow); - + sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes)); } } else if (requestType == PacketTypeNodeJsonStats) { @@ -871,36 +872,36 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { // add the node UUID nodeJson[JSON_KEY_UUID] = uuidStringWithoutCurlyBraces(node->getUUID()); - + // add the node type nodeJson[JSON_KEY_TYPE] = nodeTypeName; // add the node socket information nodeJson[JSON_KEY_PUBLIC_SOCKET] = jsonForSocket(node->getPublicSocket()); nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); - + // add the node uptime in our list nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); - + // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); if (matchingAssignment) { nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool(); - + if (!nodeData->getWalletUUID().isNull()) { TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); float pendingCreditAmount = 0; - + while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { pendingCreditAmount += i.value()->getAmount() / SATOSHIS_PER_CREDIT; ++i; } - + nodeJson[JSON_KEY_PENDING_CREDITS] = pendingCreditAmount; } } - + return nodeJson; } @@ -916,157 +917,157 @@ QString pathForAssignmentScript(const QUuid& assignmentUUID) { bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) { const QString JSON_MIME_TYPE = "application/json"; - + const QString URI_ASSIGNMENT = "/assignment"; const QString URI_NODES = "/nodes"; - + const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; - + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { if (url.path() == "/assignments.json") { // user is asking for json list of assignments - + // setup the JSON QJsonObject assignmentJSON; QJsonObject assignedNodesJSON; - + // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + if (!nodeData->getAssignmentUUID().isNull()) { // add the node using the UUID as the key QString uuidString = uuidStringWithoutCurlyBraces(nodeData->getAssignmentUUID()); assignedNodesJSON[uuidString] = jsonObjectForNode(node); } } - + assignmentJSON["fulfilled"] = assignedNodesJSON; - + QJsonObject queuedAssignmentsJSON; - + // add the queued but unfilled assignments to the json foreach(const SharedAssignmentPointer& assignment, _unfulfilledAssignments) { QJsonObject queuedAssignmentJSON; - + QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID()); queuedAssignmentJSON[JSON_KEY_TYPE] = QString(assignment->getTypeName()); - + // if the assignment has a pool, add it if (!assignment->getPool().isEmpty()) { queuedAssignmentJSON[JSON_KEY_POOL] = assignment->getPool(); } - + // add this queued assignment to the JSON queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON; } - + assignmentJSON["queued"] = queuedAssignmentsJSON; - + // print out the created JSON QJsonDocument assignmentDocument(assignmentJSON); connection->respond(HTTPConnection::StatusCode200, assignmentDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + // we've processed this request return true; } else if (url.path() == "/transactions.json") { // enumerate our pending transactions and display them in an array QJsonObject rootObject; QJsonArray transactionArray; - + TransactionHash::iterator i = _pendingAssignmentCredits.begin(); while (i != _pendingAssignmentCredits.end()) { transactionArray.push_back(i.value()->toJson()); ++i; } - + rootObject["pending_transactions"] = transactionArray; - + // print out the created JSON QJsonDocument transactionsDocument(rootObject); connection->respond(HTTPConnection::StatusCode200, transactionsDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + return true; } else if (url.path() == QString("%1.json").arg(URI_NODES)) { // setup the JSON QJsonObject rootJSON; QJsonArray nodesJSONArray; - + // enumerate the NodeList to find the assigned nodes LimitedNodeList* nodeList = LimitedNodeList::getInstance(); - + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { // add the node using the UUID as the key nodesJSONArray.append(jsonObjectForNode(node)); } - + rootJSON["nodes"] = nodesJSONArray; - + // print out the created JSON QJsonDocument nodesDocument(rootJSON); - + // send the response connection->respond(HTTPConnection::StatusCode200, nodesDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + return true; } else { // check if this is for json stats for a node const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING); QRegExp nodeShowRegex(NODE_JSON_REGEX_STRING); - + if (nodeShowRegex.indexIn(url.path()) != -1) { QUuid matchingUUID = QUuid(nodeShowRegex.cap(1)); - + // see if we have a node that matches this ID SharedNodePointer matchingNode = LimitedNodeList::getInstance()->nodeWithUUID(matchingUUID); if (matchingNode) { // create a QJsonDocument with the stats QJsonObject QJsonObject statsObject = reinterpret_cast(matchingNode->getLinkedData())->getStatsJSONObject(); - + // add the node type to the JSON data for output purposes statsObject["node_type"] = NodeType::getNodeTypeName(matchingNode->getType()).toLower().replace(' ', '-'); - + QJsonDocument statsDocument(statsObject); - + // send the response connection->respond(HTTPConnection::StatusCode200, statsDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + // tell the caller we processed the request return true; } - + return false; } - + // check if this is a request for a scripted assignment (with a temp unique UUID) const QString ASSIGNMENT_REGEX_STRING = QString("\\%1\\/(%2)\\/?$").arg(URI_ASSIGNMENT).arg(UUID_REGEX_STRING); QRegExp assignmentRegex(ASSIGNMENT_REGEX_STRING); - + if (assignmentRegex.indexIn(url.path()) != -1) { QUuid matchingUUID = QUuid(assignmentRegex.cap(1)); - + SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID); if (!matchingAssignment) { // check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID); if (pendingData) { matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID()); - + if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { // we have a matching assignment and it is for the right type, have the HTTP manager handle it // via correct URL for the script so the client can download - + QUrl scriptURL = url; scriptURL.setPath(URI_ASSIGNMENT + "/" + uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID())); - + // have the HTTPManager serve the appropriate script file return _httpManager.handleHTTPRequest(connection, scriptURL); } } } - + // request not handled return false; } @@ -1075,92 +1076,92 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url if (url.path() == URI_ASSIGNMENT) { // this is a script upload - ask the HTTPConnection to parse the form data QList formData = connection->parseFormData(); - + // check optional headers for # of instances and pool const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES"; const QString ASSIGNMENT_POOL_HEADER = "ASSIGNMENT-POOL"; - + QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); - + int numInstances = 1; - + if (!assignmentInstancesValue.isEmpty()) { // the user has requested a specific number of instances // so set that on the created assignment - + numInstances = assignmentInstancesValue.toInt(); } - + QString assignmentPool = emptyPool; QByteArray assignmentPoolValue = connection->requestHeaders().value(ASSIGNMENT_POOL_HEADER.toLocal8Bit()); - + if (!assignmentPoolValue.isEmpty()) { // specific pool requested, set that on the created assignment assignmentPool = QString(assignmentPoolValue); } - - + + for (int i = 0; i < numInstances; i++) { - + // create an assignment for this saved script Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, assignmentPool); - + QString newPath = pathForAssignmentScript(scriptAssignment->getUUID()); - + // create a file with the GUID of the assignment in the script host location QFile scriptFile(newPath); scriptFile.open(QIODevice::WriteOnly); scriptFile.write(formData[0].second); - + qDebug() << qPrintable(QString("Saved a script for assignment at %1%2") .arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool)); - + // add the script assigment to the assignment queue SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment); _unfulfilledAssignments.enqueue(sharedScriptedAssignment); _allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment); } - + // respond with a 200 code for successful upload connection->respond(HTTPConnection::StatusCode200); - + return true; } } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) { const QString ALL_NODE_DELETE_REGEX_STRING = QString("\\%1\\/?$").arg(URI_NODES); const QString NODE_DELETE_REGEX_STRING = QString("\\%1\\/(%2)\\/$").arg(URI_NODES).arg(UUID_REGEX_STRING); - + QRegExp allNodesDeleteRegex(ALL_NODE_DELETE_REGEX_STRING); QRegExp nodeDeleteRegex(NODE_DELETE_REGEX_STRING); - + if (nodeDeleteRegex.indexIn(url.path()) != -1) { // this is a request to DELETE one node by UUID - + // pull the captured string, if it exists QUuid deleteUUID = QUuid(nodeDeleteRegex.cap(1)); - + SharedNodePointer nodeToKill = LimitedNodeList::getInstance()->nodeWithUUID(deleteUUID); - + if (nodeToKill) { // start with a 200 response connection->respond(HTTPConnection::StatusCode200); - + // we have a valid UUID and node - kill the node that has this assignment QMetaObject::invokeMethod(LimitedNodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID)); - + // successfully processed request return true; } - + return true; } else if (allNodesDeleteRegex.indexIn(url.path()) != -1) { qDebug() << "Received request to kill all nodes."; LimitedNodeList::getInstance()->eraseAllNodes(); - + return true; } } - + // didn't process the request, let the HTTPManager try and handle return false; } @@ -1168,44 +1169,44 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { const QString URI_OAUTH = "/oauth"; if (url.path() == URI_OAUTH) { - + QUrlQuery codeURLQuery(url); - + const QString CODE_QUERY_KEY = "code"; QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY); - + const QString STATE_QUERY_KEY = "state"; QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY)); - - + + if (!authorizationCode.isEmpty() && !stateUUID.isNull()) { // fire off a request with this code and state to get an access token for the user - + const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token"; QUrl tokenRequestUrl = _oauthProviderURL; tokenRequestUrl.setPath(OAUTH_TOKEN_REQUEST_PATH); - + const QString OAUTH_GRANT_TYPE_POST_STRING = "grant_type=authorization_code"; QString tokenPostBody = OAUTH_GRANT_TYPE_POST_STRING; tokenPostBody += QString("&code=%1&redirect_uri=%2&client_id=%3&client_secret=%4") .arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret); - + QNetworkRequest tokenRequest(tokenRequestUrl); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - + QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit()); - + qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); - + // insert this to our pending token replies so we can associate the returned access token with the right UUID _networkReplyUUIDMap.insert(tokenReply, stateUUID); - + connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished); } - + // respond with a 200 code indicating that login is complete connection->respond(HTTPConnection::StatusCode200); - + return true; } else { return false; @@ -1217,25 +1218,25 @@ const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; void DomainServer::handleTokenRequestFinished() { QNetworkReply* networkReply = reinterpret_cast(sender()); QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - + if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { // pull the access token from the returned JSON and store it with the matching session UUID QJsonDocument returnedJSON = QJsonDocument::fromJson(networkReply->readAll()); QString accessToken = returnedJSON.object()[OAUTH_JSON_ACCESS_TOKEN_KEY].toString(); - + qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); - + // fire off a request to get this user's identity so we can see if we will let them in QUrl profileURL = _oauthProviderURL; profileURL.setPath("/api/v1/users/profile"); profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); - + QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); - + qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); - + connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); - + _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); } } @@ -1243,18 +1244,18 @@ void DomainServer::handleTokenRequestFinished() { void DomainServer::handleProfileRequestFinished() { QNetworkReply* networkReply = reinterpret_cast(sender()); QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - + if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll()); - + if (profileJSON.object()["status"].toString() == "success") { // pull the user roles from the response QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); - + QJsonArray allowedRolesArray = _argumentVariantMap.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray(); - + bool shouldAllowUserToConnect = false; - + foreach(const QJsonValue& roleValue, userRolesArray) { if (allowedRolesArray.contains(roleValue)) { // the user has a role that lets them in @@ -1263,10 +1264,10 @@ void DomainServer::handleProfileRequestFinished() { break; } } - + qDebug() << "Confirmed authentication state for user" << uuidStringWithoutCurlyBraces(matchingSessionUUID) << "-" << shouldAllowUserToConnect; - + // insert this UUID and a flag that indicates if they are allowed to connect _sessionAuthenticationHash.insert(matchingSessionUUID, shouldAllowUserToConnect); } @@ -1276,15 +1277,15 @@ void DomainServer::handleProfileRequestFinished() { void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { QUuid oldUUID = assignment->getUUID(); assignment->resetUUID(); - + qDebug() << "Reset UUID for assignment -" << *assignment.data() << "- and added to queue. Old UUID was" << uuidStringWithoutCurlyBraces(oldUUID); - + if (assignment->getType() == Assignment::AgentType && assignment->getPayload().isEmpty()) { // if this was an Agent without a script URL, we need to rename the old file so it can be retrieved at the new UUID QFile::rename(pathForAssignmentScript(oldUUID), pathForAssignmentScript(assignment->getUUID())); } - + // add the static assignment back under the right UUID, and to the queue _allAssignments.insert(assignment->getUUID(), assignment); _unfulfilledAssignments.enqueue(assignment); @@ -1296,19 +1297,19 @@ void DomainServer::nodeAdded(SharedNodePointer node) { } void DomainServer::nodeKilled(SharedNodePointer node) { - + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + if (nodeData) { // if this node's UUID matches a static assignment we need to throw it back in the assignment queue if (!nodeData->getAssignmentUUID().isNull()) { SharedAssignmentPointer matchedAssignment = _allAssignments.take(nodeData->getAssignmentUUID()); - + if (matchedAssignment && matchedAssignment->isStatic()) { refreshStaticAssignmentAndAddToQueue(matchedAssignment); } } - + // cleanup the connection secrets that we set up for this node (on the other nodes) foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) { SharedNodePointer otherNode = LimitedNodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID); @@ -1321,19 +1322,19 @@ void DomainServer::nodeKilled(SharedNodePointer node) { SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& assignmentUUID, NodeType_t nodeType) { QQueue::iterator i = _unfulfilledAssignments.begin(); - + while (i != _unfulfilledAssignments.end()) { if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == assignmentUUID) { // we have an unfulfilled assignment to return - + // return the matching assignment return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); } else { ++i; } } - + return SharedAssignmentPointer(); } @@ -1348,17 +1349,17 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType(); bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty(); bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool(); - + if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) { - + // remove the assignment from the queue SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment - _unfulfilledAssignments.begin()); - + // until we get a connection for this assignment // put assignment back in queue but stick it at the back so the others have a chance to go out _unfulfilledAssignments.enqueue(deployableAssignment); - + // stop looping, we've handed out an assignment return deployableAssignment; } else { @@ -1366,7 +1367,7 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig ++sharedAssignment; } } - + return SharedAssignmentPointer(); } @@ -1375,7 +1376,7 @@ void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPoint while (potentialMatchingAssignment != _unfulfilledAssignments.end()) { if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) { _unfulfilledAssignments.erase(potentialMatchingAssignment); - + // we matched and removed an assignment, bail out break; } else { @@ -1393,19 +1394,19 @@ void DomainServer::addStaticAssignmentsToQueue() { while (staticAssignment != staticHashCopy.end()) { // add any of the un-matched static assignments to the queue bool foundMatchingAssignment = false; - + // enumerate the nodes and check if there is one with an attached assignment with matching UUID foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { if (node->getUUID() == staticAssignment->data()->getUUID()) { foundMatchingAssignment = true; } } - + if (!foundMatchingAssignment) { // this assignment has not been fulfilled - reset the UUID and add it to the assignment queue refreshStaticAssignmentAndAddToQueue(*staticAssignment); } - + ++staticAssignment; } } From f0cb49ef5c415f2c35e0811444e0886aa099828a Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 17:24:56 -0700 Subject: [PATCH 59/99] added space between expressions in ternary operator --- libraries/shared/src/HifiConfigVariantMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 7975059736..02cce80104 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -45,7 +45,7 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL // this option is simply a switch, so add it to the map with a value of `true` mergedMap.insertMulti(key, QVariant(true)); } else { - int maxIndex = (nextKeyIndex == -1) ? argumentList.size(): nextKeyIndex; + int maxIndex = (nextKeyIndex == -1) ? argumentList.size() : nextKeyIndex; // there's at least one value associated with the option // pull the first value to start From 9e151001e032ac78ef796e3318e3942bb527d3fc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 12 Jun 2014 18:47:18 -0700 Subject: [PATCH 60/99] More object streaming refactory. --- libraries/metavoxels/src/Bitstream.cpp | 392 ++++++++++++------------- libraries/metavoxels/src/Bitstream.h | 129 +++----- 2 files changed, 216 insertions(+), 305 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index f1c927451b..760b1fc646 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -124,7 +124,7 @@ Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, Generic _position(0), _metadataType(metadataType), _genericsMode(genericsMode), - _metaObjectStreamer(*this), + _objectStreamerStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), _scriptStringStreamer(*this), @@ -198,7 +198,7 @@ void Bitstream::reset() { } Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { - WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), + WriteMappings mappings = { _objectStreamerStreamer.getAndResetTransientOffsets(), _typeStreamerStreamer.getAndResetTransientOffsets(), _attributeStreamer.getAndResetTransientOffsets(), _scriptStringStreamer.getAndResetTransientOffsets(), @@ -207,7 +207,7 @@ Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { } void Bitstream::persistWriteMappings(const WriteMappings& mappings) { - _metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets); + _objectStreamerStreamer.persistTransientOffsets(mappings.objectStreamerOffsets); _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); @@ -235,7 +235,7 @@ void Bitstream::persistAndResetWriteMappings() { } Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { - ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), + ReadMappings mappings = { _objectStreamerStreamer.getAndResetTransientValues(), _typeStreamerStreamer.getAndResetTransientValues(), _attributeStreamer.getAndResetTransientValues(), _scriptStringStreamer.getAndResetTransientValues(), @@ -244,7 +244,7 @@ Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { } void Bitstream::persistReadMappings(const ReadMappings& mappings) { - _metaObjectStreamer.persistTransientValues(mappings.metaObjectValues); + _objectStreamerStreamer.persistTransientValues(mappings.objectStreamerValues); _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); _attributeStreamer.persistTransientValues(mappings.attributeValues); _scriptStringStreamer.persistTransientValues(mappings.scriptStringValues); @@ -310,20 +310,20 @@ void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { if (!value) { - _metaObjectStreamer << NULL; + _objectStreamerStreamer << NULL; return; } const QMetaObject* metaObject = value->metaObject(); - _metaObjectStreamer << metaObject; - foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) { - propertyWriter.writeDelta(*this, value, reference); - } + const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? + static_cast(value)->getStreamer().data() : getObjectStreamers().value(metaObject); + _objectStreamerStreamer << streamer; + streamer->writeRawDelta(*this, value, reference); } void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - value = objectReader.readDelta(*this, reference); + ObjectStreamerPointer streamer; + _objectStreamerStreamer >> streamer; + value = streamer ? streamer->readRawDelta(*this, reference) : NULL; } void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) { @@ -814,38 +814,50 @@ Bitstream& Bitstream::operator>>(GenericValue& value) { Bitstream& Bitstream::operator<<(const QObject* object) { if (!object) { - _metaObjectStreamer << NULL; + _objectStreamerStreamer << NULL; return *this; } const QMetaObject* metaObject = object->metaObject(); - _metaObjectStreamer << metaObject; - foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) { - propertyWriter.write(*this, object); - } + const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? + static_cast(object)->getStreamer().data() : getObjectStreamers().value(metaObject); + _objectStreamerStreamer << streamer; + streamer->write(*this, object); return *this; } Bitstream& Bitstream::operator>>(QObject*& object) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - object = objectReader.read(*this); + ObjectStreamerPointer streamer; + _objectStreamerStreamer >> streamer; + object = streamer ? streamer->read(*this) : NULL; return *this; } Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { - _metaObjectStreamer << metaObject; + _objectStreamerStreamer << getObjectStreamers().value(metaObject); return *this; } Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - metaObject = objectReader.getMetaObject(); + ObjectStreamerPointer streamer; + _objectStreamerStreamer >> streamer; + metaObject = streamer->getMetaObject(); return *this; } -Bitstream& Bitstream::operator>>(ObjectReader& objectReader) { - _metaObjectStreamer >> objectReader; +Bitstream& Bitstream::operator<<(const ObjectStreamer* streamer) { + _objectStreamerStreamer << streamer; + return *this; +} + +Bitstream& Bitstream::operator>>(const ObjectStreamer*& streamer) { + ObjectStreamerPointer objectStreamer; + _objectStreamerStreamer >> objectStreamer; + streamer = objectStreamer.data(); + return *this; +} + +Bitstream& Bitstream::operator>>(ObjectStreamerPointer& streamer) { + _objectStreamerStreamer >> streamer; return *this; } @@ -1075,55 +1087,53 @@ Bitstream& Bitstream::operator>>(SharedObjectPointer& object) { return *this; } -Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { - if (!metaObject) { +Bitstream& Bitstream::operator<(const ObjectStreamer* streamer) { + if (!streamer) { return *this << QByteArray(); } - *this << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className())); - if (_metadataType == NO_METADATA) { - return *this; - } - const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); - *this << propertyWriters.size(); - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const PropertyWriter& propertyWriter, propertyWriters) { - _typeStreamerStreamer << propertyWriter.getStreamer(); - const QMetaProperty& property = propertyWriter.getProperty(); - if (_metadataType == FULL_METADATA) { - *this << QByteArray::fromRawData(property.name(), strlen(property.name())); - } else { - hash.addData(property.name(), strlen(property.name()) + 1); - } - } - if (_metadataType == HASH_METADATA) { - QByteArray hashResult = hash.result(); - write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + const char* name = streamer->getName(); + *this << QByteArray::fromRawData(name, strlen(name)); + if (_metadataType != NO_METADATA) { + streamer->writeMetadata(*this, _metadataType == FULL_METADATA); } return *this; } -Bitstream& Bitstream::operator>(ObjectReader& objectReader) { +Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { QByteArray className; *this >> className; if (className.isEmpty()) { - objectReader = ObjectReader(); + streamer = ObjectStreamerPointer(); return *this; } const QMetaObject* metaObject = _metaObjectSubstitutions.value(className); if (!metaObject) { metaObject = getMetaObjects().value(className); } - if (!metaObject) { - qWarning() << "Unknown class name: " << className << "\n"; + // start out with the streamer for the named class, if any + if (metaObject) { + streamer = getObjectStreamers().value(metaObject)->getSelf(); + } else { + streamer = ObjectStreamerPointer(); } if (_metadataType == NO_METADATA) { - objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject)); + if (!metaObject) { + qWarning() << "Unknown class name:" << className; + } return *this; } - int storedPropertyCount; - *this >> storedPropertyCount; - PropertyReaderVector properties(storedPropertyCount); - for (int i = 0; i < storedPropertyCount; i++) { + if (_genericsMode == ALL_GENERICS) { + streamer = readGenericObjectStreamer(className); + return *this; + } + if (!metaObject && _genericsMode == FALLBACK_GENERICS) { + streamer = readGenericObjectStreamer(className); + return *this; + } + int propertyCount; + *this >> propertyCount; + QVector properties(propertyCount); + for (int i = 0; i < propertyCount; i++) { TypeStreamerPointer typeStreamer; *this >> typeStreamer; QMetaProperty property = QMetaProperty(); @@ -1134,23 +1144,22 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { property = metaObject->property(metaObject->indexOfProperty(propertyName)); } } - properties[i] = PropertyReader(typeStreamer, property); + properties[i] = StreamerPropertyPair(typeStreamer, property); } // for hash metadata, check the names/types of the properties as well as the name hash against our own class if (_metadataType == HASH_METADATA) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; if (metaObject) { - const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); - if (propertyWriters.size() == properties.size()) { - for (int i = 0; i < propertyWriters.size(); i++) { - const PropertyWriter& propertyWriter = propertyWriters.at(i); - if (properties.at(i).getStreamer() != propertyWriter.getStreamer()) { + const QVector& localProperties = streamer->getProperties(); + if (localProperties.size() == properties.size()) { + for (int i = 0; i < localProperties.size(); i++) { + const StreamerPropertyPair& localProperty = localProperties.at(i); + if (localProperty.first != properties.at(i).first) { matches = false; break; } - const QMetaProperty& property = propertyWriter.getProperty(); - hash.addData(property.name(), strlen(property.name()) + 1); + hash.addData(localProperty.second.name(), strlen(localProperty.second.name()) + 1); } } else { matches = false; @@ -1160,11 +1169,24 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); if (metaObject && matches && localHashResult == remoteHashResult) { - objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject)); return *this; } + } else if (metaObject) { + const QVector& localProperties = streamer->getProperties(); + if (localProperties.size() != properties.size()) { + streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + return *this; + } + for (int i = 0; i < localProperties.size(); i++) { + const StreamerPropertyPair& property = properties.at(i); + if (localProperties.at(i).first != property.first || property.second.propertyIndex() != i) { + streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + return *this; + } + } + return *this; } - objectReader = ObjectReader(className, metaObject, properties); + streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); return *this; } @@ -1405,12 +1427,12 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { QPointer reference = _sharedObjectReferences.value(originID); QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; + ObjectStreamerPointer objectStreamer; + _objectStreamerStreamer >> objectStreamer; if (reference) { - objectReader.readDelta(*this, reference.data(), pointer.data()); + objectStreamer->readRawDelta(*this, reference.data(), pointer.data()); } else { - objectReader.read(*this, pointer.data()); + objectStreamer->read(*this, pointer.data()); } } else { QObject* rawObject; @@ -1445,6 +1467,31 @@ void Bitstream::clearSharedObject(QObject* object) { const int MD5_HASH_SIZE = 16; +ObjectStreamerPointer Bitstream::readGenericObjectStreamer(const QByteArray& name) { + int propertyCount; + *this >> propertyCount; + QVector properties(propertyCount); + QByteArray hash; + if (propertyCount > 0) { + for (int i = 0; i < propertyCount; i++) { + TypeStreamerPointer streamer; + *this >> streamer; + QByteArray name; + if (_metadataType == FULL_METADATA) { + *this >> name; + } + properties[i] = StreamerNamePair(streamer, name); + } + if (_metadataType == HASH_METADATA) { + hash.resize(MD5_HASH_SIZE); + read(hash.data(), hash.size() * BITS_IN_BYTE); + } + } + ObjectStreamerPointer streamer = ObjectStreamerPointer(new GenericObjectStreamer(name, properties, hash)); + static_cast(streamer.data())->_weakSelf = streamer; + return streamer; +} + TypeStreamerPointer Bitstream::readGenericTypeStreamer(const QByteArray& name, int category) { TypeStreamerPointer streamer; switch (category) { @@ -1528,6 +1575,40 @@ QMultiHash& Bitstream::getMetaObjectSubC return metaObjectSubClasses; } +const QHash& Bitstream::getObjectStreamers() { + static QHash objectStreamers = createObjectStreamers(); + return objectStreamers; +} + +QHash Bitstream::createObjectStreamers() { + QHash objectStreamers; + foreach (const QMetaObject* metaObject, getMetaObjects()) { + QVector properties; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* streamer; + if (property.isEnumType()) { + QMetaEnum metaEnum = property.enumerator(); + streamer = getEnumStreamers().value(ScopeNamePair( + QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), + QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); + } else { + streamer = getTypeStreamers().value(property.userType()); + } + if (streamer) { + properties.append(StreamerPropertyPair(streamer->getSelf(), property)); + } + } + ObjectStreamerPointer streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + streamer->_self = streamer; + objectStreamers.insert(metaObject, streamer.data()); + } + return objectStreamers; +} + QHash& Bitstream::getTypeStreamers() { static QHash typeStreamers; return typeStreamers; @@ -1577,69 +1658,17 @@ const TypeStreamer* Bitstream::createInvalidTypeStreamer() { return streamer; } -const QHash& Bitstream::getPropertyReaders() { - static QHash propertyReaders = createPropertyReaders(); - return propertyReaders; +ObjectStreamer::ObjectStreamer(const QMetaObject* metaObject) : + _metaObject(metaObject) { } -QHash Bitstream::createPropertyReaders() { - QHash propertyReaders; - foreach (const QMetaObject* metaObject, getMetaObjects()) { - PropertyReaderVector& readers = propertyReaders[metaObject]; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(ScopeNamePair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - readers.append(PropertyReader(streamer->getSelf(), property)); - } - } - } - return propertyReaders; +const QVector& ObjectStreamer::getProperties() const { + static QVector emptyProperties; + return emptyProperties; } -const QHash& Bitstream::getPropertyWriters() { - static QHash propertyWriters = createPropertyWriters(); - return propertyWriters; -} - -QHash Bitstream::createPropertyWriters() { - QHash propertyWriters; - foreach (const QMetaObject* metaObject, getMetaObjects()) { - PropertyWriterVector& writers = propertyWriters[metaObject]; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(ScopeNamePair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - writers.append(PropertyWriter(property, streamer)); - } - } - } - return propertyWriters; -} - -MappedObjectStreamer::MappedObjectStreamer(const QVector& properties) : +MappedObjectStreamer::MappedObjectStreamer(const QMetaObject* metaObject, const QVector& properties) : + ObjectStreamer(metaObject), _properties(properties) { } @@ -1647,6 +1676,10 @@ const char* MappedObjectStreamer::getName() const { return _metaObject->className(); } +const QVector& MappedObjectStreamer::getProperties() const { + return _properties; +} + void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { out << _properties.size(); if (_properties.isEmpty()) { @@ -1667,13 +1700,13 @@ void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { } } -void MappedObjectStreamer::write(Bitstream& out, QObject* object) const { +void MappedObjectStreamer::write(Bitstream& out, const QObject* object) const { foreach (const StreamerPropertyPair& property, _properties) { property.first->write(out, property.second.read(object)); } } -void MappedObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { +void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { foreach (const StreamerPropertyPair& property, _properties) { property.first->writeDelta(out, property.second.read(object), (reference && reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); @@ -1693,14 +1726,14 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { return object; } -QObject* MappedObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { +QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { if (!object && _metaObject) { object = _metaObject->newInstance(); } foreach (const StreamerPropertyPair& property, _properties) { QVariant value; - property.first->readDelta(in, value, (property.second.isValid() && reference) ? - property.second.read(reference) : QVariant()); + property.first->readDelta(in, value, (property.second.isValid() && reference && + reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); if (property.second.isValid() && object) { property.second.write(object, value); } @@ -1710,11 +1743,10 @@ QObject* MappedObjectStreamer::readDelta(Bitstream& in, const QObject* reference GenericObjectStreamer::GenericObjectStreamer(const QByteArray& name, const QVector& properties, const QByteArray& hash) : + ObjectStreamer(&GenericSharedObject::staticMetaObject), _name(name), _properties(properties), _hash(hash) { - - _metaObject = &GenericSharedObject::staticMetaObject; } const char* GenericObjectStreamer::getName() const { @@ -1744,17 +1776,22 @@ void GenericObjectStreamer::writeMetadata(Bitstream& out, bool full) const { } } -void GenericObjectStreamer::write(Bitstream& out, QObject* object) const { - const QVariantList& values = static_cast(object)->getValues(); +void GenericObjectStreamer::write(Bitstream& out, const QObject* object) const { + const QVariantList& values = static_cast(object)->getValues(); for (int i = 0; i < _properties.size(); i++) { _properties.at(i).first->write(out, values.at(i)); } } -void GenericObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { +void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { + const GenericSharedObject* genericObject = static_cast(object); + const GenericSharedObject* genericReference = (reference && + reference->metaObject() == &GenericSharedObject::staticMetaObject) ? + static_cast(reference) : NULL; for (int i = 0; i < _properties.size(); i++) { - _properties.at(i).first->writeDelta(out, static_cast(object)->getValues().at(i), reference ? - static_cast(reference)->getValues().at(i) : QVariant()); + _properties.at(i).first->writeDelta(out, genericObject->getValues().at(i), + (genericReference && genericReference->getStreamer() == genericObject->getStreamer()) ? + genericReference->getValues().at(i) : QVariant()); } } @@ -1770,7 +1807,7 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { return object; } -QObject* GenericObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { +QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { if (!object) { object = new GenericSharedObject(_weakSelf); } @@ -1786,75 +1823,6 @@ QObject* GenericObjectStreamer::readDelta(Bitstream& in, const QObject* referenc return object; } -ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, - const PropertyReaderVector& properties) : - _className(className), - _metaObject(metaObject), - _properties(properties) { -} - -QObject* ObjectReader::read(Bitstream& in, QObject* object) const { - if (!object && _metaObject) { - object = _metaObject->newInstance(); - } - foreach (const PropertyReader& property, _properties) { - property.read(in, object); - } - return object; -} - -QObject* ObjectReader::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { - if (!object && _metaObject) { - object = _metaObject->newInstance(); - } - foreach (const PropertyReader& property, _properties) { - property.readDelta(in, object, reference); - } - return object; -} - -uint qHash(const ObjectReader& objectReader, uint seed) { - return qHash(objectReader.getClassName(), seed); -} - -QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader) { - return debug << objectReader.getClassName(); -} - -PropertyReader::PropertyReader(const TypeStreamerPointer& streamer, const QMetaProperty& property) : - _streamer(streamer), - _property(property) { -} - -void PropertyReader::read(Bitstream& in, QObject* object) const { - QVariant value = _streamer->read(in); - if (_property.isValid() && object) { - _property.write(object, value); - } -} - -void PropertyReader::readDelta(Bitstream& in, QObject* object, const QObject* reference) const { - QVariant value; - _streamer->readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant()); - if (_property.isValid() && object) { - _property.write(object, value); - } -} - -PropertyWriter::PropertyWriter(const QMetaProperty& property, const TypeStreamer* streamer) : - _property(property), - _streamer(streamer) { -} - -void PropertyWriter::write(Bitstream& out, const QObject* object) const { - _streamer->write(out, _property.read(object)); -} - -void PropertyWriter::writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const { - _streamer->writeDelta(out, _property.read(object), reference && object->metaObject() == reference->metaObject() ? - _property.read(reference) : QVariant()); -} - MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : _name(name), _streamer(streamer) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 07e4b94568..10c28b9821 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -39,8 +39,6 @@ class GenericValue; class ObjectReader; class ObjectStreamer; class OwnedAttributeValue; -class PropertyReader; -class PropertyWriter; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -51,8 +49,6 @@ typedef QSharedPointer ObjectStreamerPointer; typedef QWeakPointer WeakObjectStreamerPointer; typedef QSharedPointer TypeStreamerPointer; typedef QWeakPointer WeakTypeStreamerPointer; -typedef QVector PropertyReaderVector; -typedef QVector PropertyWriterVector; typedef QPair QVariantPair; typedef QList QVariantPairList; @@ -207,7 +203,7 @@ public: class WriteMappings { public: - QHash metaObjectOffsets; + QHash objectStreamerOffsets; QHash typeStreamerOffsets; QHash attributeOffsets; QHash scriptStringOffsets; @@ -216,7 +212,7 @@ public: class ReadMappings { public: - QHash metaObjectValues; + QHash objectStreamerValues; QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; @@ -403,7 +399,10 @@ public: Bitstream& operator<<(const QMetaObject* metaObject); Bitstream& operator>>(const QMetaObject*& metaObject); - Bitstream& operator>>(ObjectReader& objectReader); + + Bitstream& operator<<(const ObjectStreamer* streamer); + Bitstream& operator>>(const ObjectStreamer*& streamer); + Bitstream& operator>>(ObjectStreamerPointer& streamer); Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); @@ -421,11 +420,11 @@ public: Bitstream& operator<<(const SharedObjectPointer& object); Bitstream& operator>>(SharedObjectPointer& object); - Bitstream& operator<(const QMetaObject* metaObject); - Bitstream& operator>(ObjectReader& objectReader); + Bitstream& operator<(const ObjectStreamer* streamer); + Bitstream& operator>(ObjectStreamerPointer& streamer); Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(TypeStreamerPointer& reader); + Bitstream& operator>(TypeStreamerPointer& streamer); Bitstream& operator<(const AttributePointer& attribute); Bitstream& operator>(AttributePointer& attribute); @@ -446,6 +445,7 @@ private slots: private: + ObjectStreamerPointer readGenericObjectStreamer(const QByteArray& name); TypeStreamerPointer readGenericTypeStreamer(const QByteArray& name, int category); QDataStream& _underlying; @@ -455,7 +455,7 @@ private: MetadataType _metadataType; GenericsMode _genericsMode; - RepeatedValueStreamer _metaObjectStreamer; + RepeatedValueStreamer _objectStreamerStreamer; RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; @@ -472,6 +472,9 @@ private: static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); + static const QHash& getObjectStreamers(); + static QHash createObjectStreamers(); + static const QHash& getEnumStreamers(); static QHash createEnumStreamers(); @@ -480,12 +483,6 @@ private: static const TypeStreamer* getInvalidTypeStreamer(); static const TypeStreamer* createInvalidTypeStreamer(); - - static const QHash& getPropertyReaders(); - static QHash createPropertyReaders(); - - static const QHash& getPropertyWriters(); - static QHash createPropertyWriters(); }; template inline void Bitstream::writeDelta(const T& value, const T& reference) { @@ -769,37 +766,46 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +typedef QPair StreamerPropertyPair; + /// Contains the information required to stream an object. class ObjectStreamer { public: - virtual const char* getName() const = 0; + ObjectStreamer(const QMetaObject* metaObject); + + const QMetaObject* getMetaObject() const { return _metaObject; } + const ObjectStreamerPointer& getSelf() const { return _self; } + + virtual const char* getName() const = 0; + virtual const QVector& getProperties() const; virtual void writeMetadata(Bitstream& out, bool full) const = 0; - virtual void write(Bitstream& out, QObject* object) const = 0; - virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const = 0; + virtual void write(Bitstream& out, const QObject* object) const = 0; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0; virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0; - virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; protected: + + friend class Bitstream; const QMetaObject* _metaObject; ObjectStreamerPointer _self; }; -typedef QPair StreamerPropertyPair; - /// A streamer that maps to a local class. class MappedObjectStreamer : public ObjectStreamer { public: - MappedObjectStreamer(const QVector& properties); + MappedObjectStreamer(const QMetaObject* metaObject, const QVector& properties); virtual const char* getName() const; + virtual const QVector& getProperties() const; virtual void writeMetadata(Bitstream& out, bool full) const; - virtual void write(Bitstream& out, QObject* object) const; - virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual void write(Bitstream& out, const QObject* object) const; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; private: @@ -816,10 +822,10 @@ public: virtual const char* getName() const; virtual void writeMetadata(Bitstream& out, bool full) const; - virtual void write(Bitstream& out, QObject* object) const; - virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual void write(Bitstream& out, const QObject* object) const; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; private: @@ -831,69 +837,6 @@ private: QByteArray _hash; }; -/// Contains the information required to read an object from the stream. -class ObjectReader { -public: - - ObjectReader(const QByteArray& className = QByteArray(), const QMetaObject* metaObject = NULL, - const QVector& properties = QVector()); - - const QByteArray& getClassName() const { return _className; } - const QMetaObject* getMetaObject() const { return _metaObject; } - - QObject* read(Bitstream& in, QObject* object = NULL) const; - QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; - - bool operator==(const ObjectReader& other) const { return _className == other._className; } - bool operator!=(const ObjectReader& other) const { return _className != other._className; } - -private: - - QByteArray _className; - const QMetaObject* _metaObject; - QVector _properties; -}; - -uint qHash(const ObjectReader& objectReader, uint seed = 0); - -QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader); - -/// Contains the information required to read an object property from the stream and apply it. -class PropertyReader { -public: - - PropertyReader(const TypeStreamerPointer& streamer = TypeStreamerPointer(), - const QMetaProperty& property = QMetaProperty()); - - const TypeStreamerPointer& getStreamer() const { return _streamer; } - - void read(Bitstream& in, QObject* object) const; - void readDelta(Bitstream& in, QObject* object, const QObject* reference) const; - -private: - - TypeStreamerPointer _streamer; - QMetaProperty _property; -}; - -/// Contains the information required to obtain an object property and write it to the stream. -class PropertyWriter { -public: - - PropertyWriter(const QMetaProperty& property = QMetaProperty(), const TypeStreamer* streamer = NULL); - - const QMetaProperty& getProperty() const { return _property; } - const TypeStreamer* getStreamer() const { return _streamer; } - - void write(Bitstream& out, const QObject* object) const; - void writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const; - -private: - - QMetaProperty _property; - const TypeStreamer* _streamer; -}; - /// Describes a metatype field. class MetaField { public: From 14f56310f605cfe67abc1b43bd7b6a6856527f21 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 23:28:43 -0700 Subject: [PATCH 61/99] Changed AccountManager to only rely on a proper OAuth response. This involved making another request after the initial OAuth authorization. Because of this now-two-step process, some methods and signals were renamed to make their purpose more clear. Additionally, a _hasProfile member variable was added to DataServerAccountInfo in order to allow for knowledge of whether the profile elements had been fetched when being deserialized from disk. Error handling for the whole process is still nonexistant. --- interface/src/Menu.cpp | 9 +- interface/src/XmppClient.cpp | 2 +- libraries/networking/src/AccountManager.cpp | 175 +++++++++++------- libraries/networking/src/AccountManager.h | 35 ++-- .../networking/src/DataServerAccountInfo.cpp | 31 +++- .../networking/src/DataServerAccountInfo.h | 17 +- 6 files changed, 172 insertions(+), 97 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3015a638f6..0b49c08f51 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -127,7 +127,7 @@ Menu::Menu() : toggleLoginMenuItem(); // connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item - connect(&accountManager, &AccountManager::accessTokenChanged, this, &Menu::toggleLoginMenuItem); + connect(&accountManager, &AccountManager::profileChanged, this, &Menu::toggleLoginMenuItem); connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem); addDisabledActionAndSeparator(fileMenu, "Scripts"); @@ -326,7 +326,7 @@ Menu::Menu() : shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); - + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true); @@ -407,7 +407,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false); addDisabledActionAndSeparator(developerMenu, "Testing"); - + QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools"); QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true); @@ -462,7 +462,7 @@ Menu::Menu() : false, appInstance->getAudio(), SLOT(toggleToneInjection())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, appInstance->getAudio(), SLOT(toggleScope())); @@ -1775,4 +1775,3 @@ QString Menu::getSnapshotsLocation() const { } return _snapshotsLocation; } - diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index 666906681c..ef9db55620 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -23,7 +23,7 @@ XmppClient::XmppClient() : _xmppMUCManager() { AccountManager& accountManager = AccountManager::getInstance(); - connect(&accountManager, SIGNAL(accessTokenChanged()), this, SLOT(connectToServer())); + connect(&accountManager, SIGNAL(profileChanged()), this, SLOT(connectToServer())); connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer())); } diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index b4aedbcb7c..918261a953 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -55,13 +55,13 @@ AccountManager::AccountManager() : { qRegisterMetaType("OAuthAccessToken"); qRegisterMetaTypeStreamOperators("OAuthAccessToken"); - + qRegisterMetaType("DataServerAccountInfo"); qRegisterMetaTypeStreamOperators("DataServerAccountInfo"); - + qRegisterMetaType("QNetworkAccessManager::Operation"); qRegisterMetaType("JSONCallbackParameters"); - + connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); } @@ -70,18 +70,18 @@ const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; void AccountManager::logout() { // a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file _accountInfo = DataServerAccountInfo(); - + emit balanceChanged(0); connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); - + QSettings settings; settings.beginGroup(ACCOUNTS_GROUP); - + QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE)); settings.remove(keyURLString); - + qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file"; - + emit logoutComplete(); // the username has changed to blank emit usernameChanged(QString()); @@ -93,7 +93,7 @@ void AccountManager::updateBalance() { JSONCallbackParameters callbackParameters; callbackParameters.jsonCallbackReceiver = &_accountInfo; callbackParameters.jsonCallbackMethod = "setBalanceFromJSON"; - + authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters); } } @@ -105,28 +105,33 @@ void AccountManager::accountInfoBalanceChanged(qint64 newBalance) { void AccountManager::setAuthURL(const QUrl& authURL) { if (_authURL != authURL) { _authURL = authURL; - + qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString()); qDebug() << "Re-setting authentication flow."; - + // check if there are existing access tokens to load from settings QSettings settings; settings.beginGroup(ACCOUNTS_GROUP); - + foreach(const QString& key, settings.allKeys()) { // take a key copy to perform the double slash replacement QString keyCopy(key); QUrl keyURL(keyCopy.replace("slashslash", "//")); - + if (keyURL == _authURL) { // pull out the stored access token and store it in memory _accountInfo = settings.value(key).value(); qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString()); - - emit accessTokenChanged(); + + // profile info isn't guaranteed to be saved too + if (_accountInfo.hasProfile()) { + emit profileChanged(); + } else { + requestProfile(); + } } } - + // tell listeners that the auth endpoint has changed emit authEndpointChanged(); } @@ -147,36 +152,36 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { - + if (!_networkAccessManager) { _networkAccessManager = new QNetworkAccessManager(this); } - + if (hasValidAccessToken()) { QNetworkRequest authenticatedRequest; - + QUrl requestURL = _authURL; - + if (path.startsWith("/")) { requestURL.setPath(path); } else { requestURL.setPath("/" + path); } - + requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); - + authenticatedRequest.setUrl(requestURL); - + if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString()); - + if (!dataByteArray.isEmpty()) { qDebug() << "The POST/PUT body -" << QString(dataByteArray); } } - + QNetworkReply* networkReply = NULL; - + switch (operation) { case QNetworkAccessManager::GetOperation: networkReply = _networkAccessManager->get(authenticatedRequest); @@ -198,24 +203,24 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray); } } - + break; default: // other methods not yet handled break; } - + if (networkReply) { if (!callbackParams.isEmpty()) { // if we have information for a callback, insert the callbackParams into our local map _pendingCallbackMap.insert(networkReply, callbackParams); - + if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) { callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)), callbackParams.updateSlot.toStdString().c_str()); } } - + // if we ended up firing of a request, hook up to it now connect(networkReply, SIGNAL(finished()), SLOT(processReply())); } @@ -224,7 +229,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: void AccountManager::processReply() { QNetworkReply* requestReply = reinterpret_cast(sender()); - + if (requestReply->error() == QNetworkReply::NoError) { passSuccessToCallback(requestReply); } else { @@ -235,17 +240,17 @@ void AccountManager::processReply() { void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); - + JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply); - + if (callbackParams.jsonCallbackReceiver) { // invoke the right method on the callback receiver QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod), Q_ARG(const QJsonObject&, jsonResponse.object())); - + // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); - + } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Received JSON response from data-server that has no matching callback."; @@ -256,13 +261,13 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply); - + if (callbackParams.errorCallbackReceiver) { // invoke the right method on the callback receiver QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod), Q_ARG(QNetworkReply::NetworkError, requestReply->error()), Q_ARG(const QString&, requestReply->errorString())); - + // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); } else { @@ -274,12 +279,12 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { } bool AccountManager::hasValidAccessToken() { - + if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString()); } - + return false; } else { return true; @@ -288,12 +293,12 @@ bool AccountManager::hasValidAccessToken() { bool AccountManager::checkAndSignalForAccessToken() { bool hasToken = hasValidAccessToken(); - + if (!hasToken) { // emit a signal so somebody can call back to us and request an access token given a username and password emit authRequired(); } - + return hasToken; } @@ -304,36 +309,36 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas } QNetworkRequest request; - + QUrl grantURL = _authURL; grantURL.setPath("/oauth/token"); - + const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; - + QByteArray postData; postData.append("grant_type=password&"); postData.append("username=" + login + "&"); postData.append("password=" + QUrl::toPercentEncoding(password) + "&"); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); - + request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - + QNetworkReply* requestReply = _networkAccessManager->post(request, postData); - connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } -void AccountManager::requestFinished() { +void AccountManager::requestAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); - + QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); const QJsonObject& rootObject = jsonResponse.object(); - + if (!rootObject.contains("error")) { // construct an OAuthAccessToken from the json object - + if (!rootObject.contains("access_token") || !rootObject.contains("expires_in") || !rootObject.contains("token_type")) { // TODO: error handling - malformed token response @@ -342,23 +347,21 @@ void AccountManager::requestFinished() { // clear the path from the response URL so we have the right root URL for this access token QUrl rootURL = requestReply->url(); rootURL.setPath(""); - + qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString()); - - _accountInfo = DataServerAccountInfo(rootObject); - + + _accountInfo = DataServerAccountInfo(); + _accountInfo.setAccessTokenFromJSON(rootObject); + emit loginComplete(rootURL); - // the username has changed to whatever came back - emit usernameChanged(_accountInfo.getUsername()); - - // we have found or requested an access token - emit accessTokenChanged(); - + // store this access token into the local settings QSettings localSettings; localSettings.beginGroup(ACCOUNTS_GROUP); localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), QVariant::fromValue(_accountInfo)); + + requestProfile(); } } else { // TODO: error handling @@ -367,7 +370,53 @@ void AccountManager::requestFinished() { } } -void AccountManager::requestError(QNetworkReply::NetworkError error) { +void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { // TODO: error handling qDebug() << "AccountManager requestError - " << error; } + +void AccountManager::requestProfile() { + if (!_networkAccessManager) { + _networkAccessManager = new QNetworkAccessManager(this); + } + + QUrl profileURL = _authURL; + profileURL.setPath("/api/v1/users/profile"); + profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); + + QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); + connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished); + connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); +} + +void AccountManager::requestProfileFinished() { + QNetworkReply* profileReply = reinterpret_cast(sender()); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(profileReply->readAll()); + const QJsonObject& rootObject = jsonResponse.object(); + + if (rootObject.contains("status") && rootObject["status"].toString() == "success") { + _accountInfo.setProfileInfoFromJSON(rootObject); + + emit profileChanged(); + + // the username has changed to whatever came back + emit usernameChanged(_accountInfo.getUsername()); + + // store the whole profile into the local settings + QUrl rootURL = profileReply->url(); + rootURL.setPath(""); + QSettings localSettings; + localSettings.beginGroup(ACCOUNTS_GROUP); + localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), + QVariant::fromValue(_accountInfo)); + } else { + // TODO: error handling + qDebug() << "Error in response for profile"; + } +} + +void AccountManager::requestProfileError(QNetworkReply::NetworkError error) { + // TODO: error handling + qDebug() << "AccountManager requestProfileError - " << error; +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 628b084ea8..c18836ca54 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -23,9 +23,9 @@ class JSONCallbackParameters { public: JSONCallbackParameters(); - + bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; } - + QObject* jsonCallbackReceiver; QString jsonCallbackMethod; QObject* errorCallbackReceiver; @@ -38,30 +38,33 @@ class AccountManager : public QObject { Q_OBJECT public: static AccountManager& getInstance(); - + void authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), const QByteArray& dataByteArray = QByteArray(), QHttpMultiPart* dataMultiPart = NULL); - + const QUrl& getAuthURL() const { return _authURL; } void setAuthURL(const QUrl& authURL); bool hasAuthEndpoint() { return !_authURL.isEmpty(); } - + bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); } bool hasValidAccessToken(); Q_INVOKABLE bool checkAndSignalForAccessToken(); - + void requestAccessToken(const QString& login, const QString& password); - + void requestProfile(); + const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } - + void destroy() { delete _networkAccessManager; } - + public slots: - void requestFinished(); - void requestError(QNetworkReply::NetworkError error); + void requestAccessTokenFinished(); + void requestProfileFinished(); + void requestAccessTokenError(QNetworkReply::NetworkError error); + void requestProfileError(QNetworkReply::NetworkError error); void logout(); void updateBalance(); void accountInfoBalanceChanged(qint64 newBalance); @@ -69,7 +72,7 @@ signals: void authRequired(); void authEndpointChanged(); void usernameChanged(const QString& username); - void accessTokenChanged(); + void profileChanged(); void loginComplete(const QUrl& authURL); void loginFailed(); void logoutComplete(); @@ -80,19 +83,19 @@ private: AccountManager(); AccountManager(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented - + void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); - + Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart); - + QUrl _authURL; QNetworkAccessManager* _networkAccessManager; QMap _pendingCallbackMap; - + DataServerAccountInfo _accountInfo; }; diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 809f083e35..7f3b40ab82 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -19,11 +19,13 @@ DataServerAccountInfo::DataServerAccountInfo() : _xmppPassword(), _discourseApiKey(), _balance(0), - _hasBalance(false) + _hasBalance(false), + _hasProfile(false) { - + } +/* DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : _accessToken(jsonObject), _username(), @@ -36,6 +38,7 @@ DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : setXMPPPassword(userJSONObject["xmpp_password"].toString()); setDiscourseApiKey(userJSONObject["discourse_api_key"].toString()); } +*/ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) { _accessToken = otherInfo._accessToken; @@ -44,6 +47,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _discourseApiKey = otherInfo._discourseApiKey; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; + _hasProfile = otherInfo._hasProfile; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -54,19 +58,24 @@ DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountI void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { using std::swap; - + swap(_accessToken, otherInfo._accessToken); swap(_username, otherInfo._username); swap(_xmppPassword, otherInfo._xmppPassword); swap(_discourseApiKey, otherInfo._discourseApiKey); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); + swap(_hasProfile, otherInfo._hasProfile); +} + +void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { + _accessToken = OAuthAccessToken(jsonObject); } void DataServerAccountInfo::setUsername(const QString& username) { if (_username != username) { _username = username; - + qDebug() << "Username changed to" << username; } } @@ -87,7 +96,7 @@ void DataServerAccountInfo::setBalance(qint64 balance) { if (!_hasBalance || _balance != balance) { _balance = balance; _hasBalance = true; - + emit balanceChanged(_balance); } } @@ -99,12 +108,20 @@ void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { } } +void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject) { + QJsonObject user = jsonObject["data"].toObject()["user"].toObject(); + setUsername(user["username"].toString()); + setXMPPPassword(user["xmpp_password"].toString()); + setDiscourseApiKey(user["discourse_api_key"].toString()); + setHasProfile(true); +} + QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._hasProfile; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._hasProfile; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index e0209326f9..27999aba2f 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -22,12 +22,13 @@ class DataServerAccountInfo : public QObject { Q_OBJECT public: DataServerAccountInfo(); - DataServerAccountInfo(const QJsonObject& jsonObject); + // DataServerAccountInfo(const QJsonObject& jsonObject); DataServerAccountInfo(const DataServerAccountInfo& otherInfo); DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo); - + const OAuthAccessToken& getAccessToken() const { return _accessToken; } - + void setAccessTokenFromJSON(const QJsonObject& jsonObject); + const QString& getUsername() const { return _username; } void setUsername(const QString& username); @@ -36,26 +37,32 @@ public: const QString& getDiscourseApiKey() const { return _discourseApiKey; } void setDiscourseApiKey(const QString& discourseApiKey); - + qint64 getBalance() const { return _balance; } void setBalance(qint64 balance); bool hasBalance() const { return _hasBalance; } void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); + bool hasProfile() const { return _hasProfile; } + void setHasProfile(bool hasProfile) { _hasProfile = hasProfile; } + + void setProfileInfoFromJSON(const QJsonObject& jsonObject); + friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info); friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info); signals: qint64 balanceChanged(qint64 newBalance); private: void swap(DataServerAccountInfo& otherInfo); - + OAuthAccessToken _accessToken; QString _username; QString _xmppPassword; QString _discourseApiKey; qint64 _balance; bool _hasBalance; + bool _hasProfile; }; #endif // hifi_DataServerAccountInfo_h From e4a0275f57fdc2455bdc4880e37a5e828dcd21ce Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 10:53:11 -0700 Subject: [PATCH 62/99] Exposed sixense and mouse options and oculus UI angle to preferences --- interface/src/Menu.cpp | 6 +- interface/src/Menu.h | 10 +- interface/src/devices/OculusManager.h | 2 + interface/src/devices/SixenseManager.cpp | 4 +- interface/src/devices/SixenseManager.h | 3 + interface/src/ui/ApplicationOverlay.cpp | 8 +- interface/src/ui/ApplicationOverlay.h | 5 +- interface/src/ui/PreferencesDialog.cpp | 13 + interface/ui/preferencesDialog.ui | 331 ++++++++++++++++++++++- 9 files changed, 367 insertions(+), 15 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3015a638f6..644b08f03d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,7 @@ #include "ui/ModelsBrowser.h" #include "ui/LoginDialog.h" #include "ui/NodeBounds.h" +#include "devices/OculusManager.h" Menu* Menu::_instance = NULL; @@ -83,6 +84,7 @@ Menu::Menu() : _audioJitterBufferSamples(0), _bandwidthDialog(NULL), _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), + _realWorldFieldOfView(DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), _faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION), _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), @@ -91,6 +93,9 @@ Menu::Menu() : _lodToolsDialog(NULL), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), + _oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE), + _sixenseReticleMoveSpeed(DEFAULT_SIXENSE_RETICLE_MOVE_SPEED), + _invertSixenseButtons(DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS), _automaticAvatarLOD(true), _avatarLODDecreaseFPS(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS), _avatarLODIncreaseFPS(ADJUST_LOD_UP_FPS), @@ -387,7 +392,6 @@ Menu::Menu() : QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 41f0b41498..4d2174a448 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -90,6 +90,12 @@ public: void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; } float getRealWorldFieldOfView() const { return _realWorldFieldOfView; } void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; } + float getOculusUIAngularSize() const { return _oculusUIAngularSize; } + void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } + float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; } + void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; } + bool getInvertSixenseButtons() const { return _invertSixenseButtons; } + void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; } float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; } void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; } @@ -255,6 +261,9 @@ private: LodToolsDialog* _lodToolsDialog; int _maxVoxels; float _voxelSizeScale; + float _oculusUIAngularSize; + float _sixenseReticleMoveSpeed; + bool _invertSixenseButtons; bool _automaticAvatarLOD; float _avatarLODDecreaseFPS; float _avatarLODIncreaseFPS; @@ -400,7 +409,6 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; - const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index caff38a6b0..21b9d67f4d 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -20,6 +20,8 @@ #include "renderer/ProgramObject.h" +const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; + class Camera; /// Handles interaction with the Oculus Rift. diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index fa902be46f..5257a777e0 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -339,7 +339,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseInvertInputButtons)) { + if (Menu::getInstance()->getInvertSixenseButtons()) { bumperButton = Qt::LeftButton; triggerButton = Qt::RightButton; } else { @@ -351,7 +351,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - float cursorRange = widget->width(); + float cursorRange = widget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; pos.setX(cursorRange * xAngle); pos.setY(cursorRange * yAngle); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index f3c5633f90..71511cd06b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -30,6 +30,9 @@ const unsigned int BUTTON_FWD = 1U << 7; // Event type that represents moving the controller const unsigned int CONTROLLER_MOVE_EVENT = 1500U; +const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 1.0f; +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = true; + /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { Q_OBJECT diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 95ac803e37..a45e42dc0b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -33,7 +33,7 @@ ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), - _textureFov(PI / 2.5f), + _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _uiType(HEMISPHERE) { } @@ -50,6 +50,8 @@ const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE; + Application* application = Application::getInstance(); Overlays& overlays = application->getOverlays(); @@ -154,8 +156,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - - // Get vertical FoV of the displayed overlay texture const float halfVerticalAngle = _oculusAngle / 2.0f; const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); @@ -338,7 +338,7 @@ void ApplicationOverlay::renderControllerPointer() { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - float cursorRange = glWidget->width(); + float cursorRange = glWidget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 03a323cd5d..b6066099fa 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -31,10 +31,7 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); - float getOculusAngle() const { return _oculusAngle; } - - // Setters - void setOculusAngle(float oculusAngle) { _oculusAngle = oculusAngle; } + void setUIType(UIType uiType) { _uiType = uiType; } private: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index d5c6079d4b..5e6c6984eb 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -137,6 +137,13 @@ void PreferencesDialog::loadPreferences() { ui.maxVoxelsSpin->setValue(menuInstance->getMaxVoxels()); ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond()); + + ui.oculusUIAngularSizeSpin->setValue(menuInstance->getOculusUIAngularSize()); + + ui.sixenseReticleMoveSpeedSpin->setValue(menuInstance->getSixenseReticleMoveSpeed()); + + ui.invertSixenseButtonsCheckBox->setChecked(menuInstance->getInvertSixenseButtons()); + } void PreferencesDialog::savePreferences() { @@ -189,6 +196,12 @@ void PreferencesDialog::savePreferences() { (float)ui.faceshiftEyeDeflectionSider->maximum()); Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value()); + Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value()); + + Menu::getInstance()->setSixenseReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); + + Menu::getInstance()->setInvertSixenseButtons(ui.invertSixenseButtonsCheckBox->isChecked()); + Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value()); Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value()); diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index a1c2073ab6..f00d7c4788 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -154,9 +154,9 @@ color: #0e7077 0 - -204 - 494 - 1091 + -1002 + 477 + 1386 @@ -1605,6 +1605,331 @@ padding: 10px;margin-top:10px + + + + + 0 + 0 + + + + + 0 + 40 + + + + + Arial + 20 + 50 + false + + + + color: #0e7077 + + + Oculus Rift + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + User Interface Angular Size + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 125 + 36 + + + + + Arial + + + + 30 + + + 160 + + + 1 + + + 72 + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + Arial + 20 + 50 + false + + + + color: #0e7077 + + + Sixense Controllers + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + Invert Mouse Buttons + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + + 0 + 0 + + + + + + + + 32 + 32 + + + + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + Reticle Movement Speed + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 125 + 36 + + + + + Arial + + + + 100 + + + 1 + + + 50 + + + + + From 4a1307fa382ff92a25877f21474c57c42a55825d Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 10:54:50 -0700 Subject: [PATCH 63/99] Killed two warnings --- interface/src/ui/ApplicationOverlay.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a45e42dc0b..f6afc15223 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -316,7 +316,6 @@ void ApplicationOverlay::renderControllerPointer() { MyAvatar* myAvatar = application->getAvatar(); const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); - int numberOfPalms = handData->getNumPalms(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; @@ -431,8 +430,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) magnifyHeight = widgetHeight - mouseY; } - const float halfMagnifyHeight = magnifyHeight / 2.0f; - float newWidth = magnifyWidth * magnification; float newHeight = magnifyHeight * magnification; From 3af4e32c813260b4edf55c60d3294cc032f9d58d Mon Sep 17 00:00:00 2001 From: John Grosen Date: Fri, 13 Jun 2014 11:08:38 -0700 Subject: [PATCH 64/99] Now determines hasProfile from presence of username --- libraries/networking/src/DataServerAccountInfo.cpp | 14 +++++++------- libraries/networking/src/DataServerAccountInfo.h | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 7f3b40ab82..99d92d8738 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -19,8 +19,7 @@ DataServerAccountInfo::DataServerAccountInfo() : _xmppPassword(), _discourseApiKey(), _balance(0), - _hasBalance(false), - _hasProfile(false) + _hasBalance(false) { } @@ -47,7 +46,6 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _discourseApiKey = otherInfo._discourseApiKey; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; - _hasProfile = otherInfo._hasProfile; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -65,7 +63,6 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_discourseApiKey, otherInfo._discourseApiKey); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); - swap(_hasProfile, otherInfo._hasProfile); } void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { @@ -108,20 +105,23 @@ void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { } } +bool DataServerAccountInfo::hasProfile() const { + return _username.length() > 0; +} + void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject) { QJsonObject user = jsonObject["data"].toObject()["user"].toObject(); setUsername(user["username"].toString()); setXMPPPassword(user["xmpp_password"].toString()); setDiscourseApiKey(user["discourse_api_key"].toString()); - setHasProfile(true); } QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._hasProfile; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._hasProfile; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 27999aba2f..8ea7972392 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -44,8 +44,7 @@ public: void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); - bool hasProfile() const { return _hasProfile; } - void setHasProfile(bool hasProfile) { _hasProfile = hasProfile; } + bool hasProfile() const; void setProfileInfoFromJSON(const QJsonObject& jsonObject); @@ -62,7 +61,6 @@ private: QString _discourseApiKey; qint64 _balance; bool _hasBalance; - bool _hasProfile; }; #endif // hifi_DataServerAccountInfo_h From 422db14812a20a63cb7c3a987733832253c7e883 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 11:34:57 -0700 Subject: [PATCH 65/99] Fixed some improper logic and used better defaults --- interface/src/devices/SixenseManager.cpp | 17 +++++++++++------ interface/src/devices/SixenseManager.h | 5 +++-- interface/src/ui/ApplicationOverlay.cpp | 13 ++++++------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 5257a777e0..ed55dc4c66 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,6 +185,11 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } +float SixenseManager::getCursorPixelRangeMultiplier() const { + //scales (0,100) to (0.4,2.0) + return ((Menu::getInstance()->getSixenseReticleMoveSpeed()) * 0.008f + 0.2f) * 2.0f; +} + #ifdef HAVE_SIXENSE // the calibration sequence is: @@ -347,14 +352,14 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { triggerButton = Qt::LeftButton; } - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - float cursorRange = widget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; + float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); - pos.setX(cursorRange * xAngle); - pos.setY(cursorRange * yAngle); + pos.setX(widget->width() / 2.0f + cursorRange * xAngle); + pos.setY(widget->height() / 2.0f + cursorRange * yAngle); //If we are off screen then we should stop processing, and if a trigger or bumper is pressed, //we should unpress them. diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 71511cd06b..41e061a25b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -30,8 +30,8 @@ const unsigned int BUTTON_FWD = 1U << 7; // Event type that represents moving the controller const unsigned int CONTROLLER_MOVE_EVENT = 1500U; -const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 1.0f; -const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = true; +const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 37.5f; +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { @@ -42,6 +42,7 @@ public: ~SixenseManager(); void update(float deltaTime); + float getCursorPixelRangeMultiplier() const; public slots: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f6afc15223..8ba0b42910 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -333,14 +333,14 @@ void ApplicationOverlay::renderControllerPointer() { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) ; + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - float cursorRange = glWidget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); - int mouseX = cursorRange * xAngle; - int mouseY = cursorRange * yAngle; + int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; + int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { @@ -409,7 +409,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; const float halfHorizontalAngle = horizontalAngle / 2; - float magnifyWidth = 80.0f; float magnifyHeight = 60.0f; From 9fc84d6358abe2942cf8808faba84a56feda0588 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 13 Jun 2014 12:00:42 -0700 Subject: [PATCH 66/99] Generics up and running. --- libraries/metavoxels/src/Bitstream.cpp | 33 ++++++++++++++++++------- libraries/metavoxels/src/Bitstream.h | 6 ++++- libraries/metavoxels/src/SharedObject.h | 2 ++ tests/metavoxels/src/MetavoxelTests.cpp | 29 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 760b1fc646..80aa07b026 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -760,8 +760,7 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { - _typeStreamerStreamer << streamer->getStreamerToWrite(value); - streamer->write(*this, value); + streamer->writeVariant(*this, value); } else { qWarning() << "Non-streamable type: " << value.typeName() << "\n"; } @@ -774,7 +773,7 @@ Bitstream& Bitstream::operator>>(QVariant& value) { if (!streamer) { value = QVariant(); } else { - value = streamer->read(*this); + value = streamer->readVariant(*this); } return *this; } @@ -1889,6 +1888,15 @@ QVariant TypeStreamer::read(Bitstream& in) const { return QVariant(); } +void TypeStreamer::writeVariant(Bitstream& out, const QVariant& value) const { + out << this; + write(out, value); +} + +QVariant TypeStreamer::readVariant(Bitstream& in) const { + return read(in); +} + void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { if (value == reference) { out << false; @@ -2151,6 +2159,10 @@ const char* GenericTypeStreamer::getName() const { return _name.constData(); } +QVariant GenericTypeStreamer::readVariant(Bitstream& in) const { + return QVariant::fromValue(GenericValue(_weakSelf, read(in))); +} + GenericEnumTypeStreamer::GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, int bits, const QByteArray& hash) : GenericTypeStreamer(name), @@ -2190,7 +2202,7 @@ void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const QVariant GenericEnumTypeStreamer::read(Bitstream& in) const { int intValue = 0; in.read(&intValue, _bits); - return QVariant::fromValue(GenericValue(_weakSelf, intValue)); + return intValue; } TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { @@ -2270,7 +2282,7 @@ QVariant GenericStreamableTypeStreamer::read(Bitstream& in) const { foreach (const StreamerNamePair& field, _fields) { values.append(field.first->read(in)); } - return QVariant::fromValue(GenericValue(_weakSelf, values)); + return values; } TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { @@ -2337,7 +2349,7 @@ QVariant GenericListTypeStreamer::read(Bitstream& in) const { for (int i = 0; i < size; i++) { values.append(_valueStreamer->read(in)); } - return QVariant::fromValue(GenericValue(_weakSelf, values)); + return values; } TypeStreamer::Category GenericListTypeStreamer::getCategory() const { @@ -2443,13 +2455,16 @@ QVariant GenericMapTypeStreamer::read(Bitstream& in) const { QVariant value = _valueStreamer->read(in); values.append(QVariantPair(key, value)); } - return QVariant::fromValue(GenericValue(_weakSelf, QVariant::fromValue(values))); + return QVariant::fromValue(values); } TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { return MAP_CATEGORY; } -const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { - return value.value().getStreamer().data(); +void GenericValueStreamer::writeVariant(Bitstream& out, const QVariant& value) const { + GenericValue genericValue = value.value(); + out << genericValue.getStreamer().data(); + genericValue.getStreamer()->write(out, genericValue.getValue()); } + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 10c28b9821..1589473b0e 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -918,6 +918,9 @@ public: virtual void write(Bitstream& out, const QVariant& value) const; virtual QVariant read(Bitstream& in) const; + virtual void writeVariant(Bitstream& out, const QVariant& value) const; + virtual QVariant readVariant(Bitstream& in) const; + virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; @@ -1031,6 +1034,7 @@ public: GenericTypeStreamer(const QByteArray& name); virtual const char* getName() const; + virtual QVariant readVariant(Bitstream& in) const; protected: @@ -1260,7 +1264,7 @@ private: class GenericValueStreamer : public SimpleTypeStreamer { public: - virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; + virtual void writeVariant(Bitstream& out, const QVariant& value) const; }; /// Macro for registering simple type streamers. diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 41c3c01ffe..15cd5eb0a1 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -41,6 +41,8 @@ public: /// Returns the unique local ID for this object. int getID() const { return _id; } + void setID(int id) { _weakHash.insert(_id = id, this); } + /// Returns the local origin ID for this object. int getOriginID() const { return _originID; } diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 6ec2331b14..6bd99a6c82 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -214,6 +214,35 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { return true; } + // go back to the beginning and read everything as generics + inStream.device()->seek(0); + Bitstream genericIn(inStream, metadataType, Bitstream::ALL_GENERICS); + genericIn >> testObjectReadA; + genericIn >> testObjectReadB; + genericIn >> messageRead; + genericIn >> endRead; + + // reassign the ids + testObjectReadA->setID(testObjectWrittenA->getID()); + testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); + testObjectReadB->setID(testObjectWrittenB->getID()); + testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); + + // write it back out and compare + QByteArray compareArray; + QDataStream compareOutStream(&compareArray, QIODevice::WriteOnly); + Bitstream compareOut(compareOutStream, metadataType); + compareOut << testObjectReadA; + compareOut << testObjectReadB; + compareOut << messageRead; + compareOut << endRead; + compareOut.flush(); + + if (array != compareArray) { + qDebug() << "Mismatch between written/generic written streams."; + return true; + } + return false; } From 10875c5e61cfb056e0bbe1f260d8cd154e61b6fe Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 13 Jun 2014 12:12:17 -0700 Subject: [PATCH 67/99] Fix for setID. --- libraries/metavoxels/src/SharedObject.cpp | 5 +++++ libraries/metavoxels/src/SharedObject.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 47d69f4abe..05af5f1bf8 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -30,6 +30,11 @@ SharedObject::SharedObject() : _weakHash.insert(_id, this); } +void SharedObject::setID(int id) { + _weakHash.remove(_id); + _weakHash.insert(_id = id, this); +} + void SharedObject::incrementReferenceCount() { _referenceCount.ref(); } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 15cd5eb0a1..ba643b449c 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -41,7 +41,7 @@ public: /// Returns the unique local ID for this object. int getID() const { return _id; } - void setID(int id) { _weakHash.insert(_id = id, this); } + void setID(int id); /// Returns the local origin ID for this object. int getOriginID() const { return _originID; } From 3747a5eeae79bee2b14b6bd43db226ac37316808 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 13:38:28 -0700 Subject: [PATCH 68/99] update hemisphere VBO on ui size change --- interface/src/ui/ApplicationOverlay.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8ba0b42910..e228e39976 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -692,8 +692,11 @@ void ApplicationOverlay::renderTexturedHemisphere() { static VerticesIndices vbo(0, 0); int vertices = slices * (stacks - 1) + 1; int indices = slices * 2 * 3 * (stacks - 2) + slices * 3; - //We only generate the VBO once - if (vbo.first == 0) { + + static float oldTextureFOV = _textureFov; + //We only generate the VBO when the _textureFov changes + if (vbo.first == 0 || oldTextureFOV != _textureFov) { + oldTextureFOV = _textureFov; TextureVertex* vertexData = new TextureVertex[vertices]; TextureVertex* vertex = vertexData; for (int i = 0; i < stacks - 1; i++) { @@ -718,7 +721,9 @@ void ApplicationOverlay::renderTexturedHemisphere() { vertex->uv.y = 0.5f; vertex++; - glGenBuffers(1, &vbo.first); + if (vbo.first == 0){ + glGenBuffers(1, &vbo.first); + } glBindBuffer(GL_ARRAY_BUFFER, vbo.first); const int BYTES_PER_VERTEX = sizeof(TextureVertex); glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); From 230af7f5a7820ddf632c28bf2258f279440fd537 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 13:49:35 -0700 Subject: [PATCH 69/99] Stopped clamping the magnification --- interface/src/ui/ApplicationOverlay.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index e228e39976..830a3f843b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -415,20 +415,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) mouseX -= magnifyWidth / 2; mouseY -= magnifyHeight / 2; - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - float newWidth = magnifyWidth * magnification; float newHeight = magnifyHeight * magnification; From 1a3de1ef28d343146b9c2312466d4ad7f5586e91 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Fri, 13 Jun 2014 15:01:20 -0700 Subject: [PATCH 70/99] Totally removed DataServerAccountInfo's JSON constructor --- .../networking/src/DataServerAccountInfo.cpp | 15 --------------- libraries/networking/src/DataServerAccountInfo.h | 1 - 2 files changed, 16 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 99d92d8738..c60a17e0d8 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -24,21 +24,6 @@ DataServerAccountInfo::DataServerAccountInfo() : } -/* -DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : - _accessToken(jsonObject), - _username(), - _xmppPassword(), - _balance(0), - _hasBalance(false) -{ - QJsonObject userJSONObject = jsonObject["user"].toObject(); - setUsername(userJSONObject["username"].toString()); - setXMPPPassword(userJSONObject["xmpp_password"].toString()); - setDiscourseApiKey(userJSONObject["discourse_api_key"].toString()); -} -*/ - DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) { _accessToken = otherInfo._accessToken; _username = otherInfo._username; diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 8ea7972392..5800942376 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -22,7 +22,6 @@ class DataServerAccountInfo : public QObject { Q_OBJECT public: DataServerAccountInfo(); - // DataServerAccountInfo(const QJsonObject& jsonObject); DataServerAccountInfo(const DataServerAccountInfo& otherInfo); DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo); From c593117ac5bccd39d09625298d54c596846bf411 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 15:03:09 -0700 Subject: [PATCH 71/99] New reticle for sixense and oculus mouse movement --- .../resources/images/sixense-reticle.png | Bin 0 -> 26350 bytes interface/src/ui/ApplicationOverlay.cpp | 62 ++++++++++-------- interface/src/ui/ApplicationOverlay.h | 2 + 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 interface/resources/images/sixense-reticle.png diff --git a/interface/resources/images/sixense-reticle.png b/interface/resources/images/sixense-reticle.png new file mode 100644 index 0000000000000000000000000000000000000000..b168b25106b99693112c3b2f3a351b4a49733325 GIT binary patch literal 26350 zcmXtfc{G&o`~NdDma$JLBg7OkBFo#FeIy}@8rhd7B*u~?>x`vHLK0a*3N?1JWF3_) zYspO3nUImOkA0c>oj%|5d(OH4c%HxR`+BbPI`{Qjo?Nps<>Nld4FCY2+2xBi00091 zEhq;708aPbdjbH!am&K=BJjUw&$t-}0039OW&0oi;3NNAAe3CrLjV9c@0(ega87gZ z9amQ2YcC%L04c!iqS1|q<4dg*55!F$Xf&QtJ#!`nt(7Kq(d>rfN|yH(*WIqRKaX6a zk1p`F;v;`IN3qe5GkMygS?YS5ztS8FFLDEc1cGii;>>*EX}n>LAs}rSW7y?TO}HFm zD5Ki$k2`>cpD9=8^|AZXl+Pp39^=mEBZYV8^Ra_~{`c`}pJWvK&fPZ_QByGZNvNA$ zKH~lNa4?e)SHM{V|M$+Z5J^-T59m1P!#lgKwY7^JvE2FCl*1Gy5ako7(2A=-e5P&Qw0nI#1MBkm(sgFBeHF)01XG7MYiqX}u3HDY1Sn(wt z)Y`3iWlH~)x$JHS0Zn=j?{rv+R|#?cLI4+Av8?H-pNN@#05p zjvD8~I8@L7l4>O5=+(iABPG2{lGFit80Q4c|D2OE;EFvvApBZzj)s2LAE3A_gi z?z=dv-Wf!2WZoa-QRVlM!Vht5MxMJW?t?YT0}SVv-Zd!hVmLP=KV1#Ock5ZO-WU;0 zz7A_&lnOf^UB^7n)z}>H;7@8S`uJx=oC&DQosTWx+#kIa`!MpiX4|Qi=VKQ0>i^*+ zbzCL2ZS@z}_XZ({h3QhnQJ(1Om-}>~7%}Kv@`XF%c=BTyH_!ueyXz9f*zvfks9?jn z?Iqy%*$`+L9CzH4(6X6!*gXa$@%`%N-!!)_k-XZO` za>u|>O*@fR%)!uhIm`E9D<}icYueJ(l`O=gti4(^l;lipZR{xE*53r3N`~NIYf}k& zf1{__4{K^g?8ANjIupZJ*QUOocJnD8n!Fm9T8~?4Z-Z?5w>BAYA~sK`XyHh!2{QNf zP*sU;&*fifO~|li^0MU)DDYlXh7nEK+v>h;iVs%;L_(G2@^>)kl+iY8>GqCZW1(MytXDV1iuIQtU zIm9p?c7dxP3)kSli|R z&&u<);7$%3<`Irr{KVzgLhRx!JE;}0T`hE_EUY>v8p;7~)S#MnB|7@F&*PcVd$lk3 zNeu!A8*To74oFj=7WNQwu=Clth5qn2>4FYE#pyFP=DpfCbAvkJ*Ugd9u)DfLr4bZ= z-64zv-3t1qDBTu`w^{6G$0U{i;iT*wL$D#@S}N%8(^VIn&VP3>g=0|2P9qpq3R<;# zpx>os2(?a9S=UY3ZCgEt(j8*xQg-w#1S3%a$5B@$HmBl9PmbG>sW4z$;;ew%u$Q2z!NUuj46s32?rD{QA zhIfuqVS;d*HChDKvElbc1ORO4R<6q0p{2r%zGNX{AjUxWd@u#pu_p6(I7)o4aXApPC3G@pxao0|4`nEYh=FOqj zP{h5#P{9JRzxuSG+T8sJ$z=_u|6HHdg2zEX<_(@-1pM;&bJH%)j z^Cd+dEH^lhsBr_--Q8Uqfl&Jl0H~^|xo0!g*xXDvL$+4XTZ+9?X`$N=)i&QM3Gx6w z6(-1LWXRWu8DiL&d!J-8^6YV2f}t)!tJ$8ze?XB%Khr>nxr?H|%3?|5#?Y-=mFK-( zo1^0uV>*w8oybIz;3Rz)kGmSZ`YrOV^x?12ho?l>aPFtxsdsm$h*KR|r_ybpfN!d$ z8S*%-MF<5Q)b^RE_nxTtOnQl(m`|!kVtnO`axWv#<}z|sY@vw63i`JSy7^$}l$zyJ z*xlv|jE#8*oStr@npnsWTu7>($@Ky7M@Nx%E-s{;oSgm6ry~0uv79FX>JW~}2-lmW zMD9p{0WB>pLq|u)J6>L#%d4wzx3+?*xyISWmywV7R35@H`8JE9xcs~2U*r-$OF?f9 zF>HnyHmJ*&kOMJN@V&P7r30pF(fubjO~Q|VC)i{^c8ABi!`Y1tdvnn(p=HM~LZ!q@ z$KB#m$!EY8Gv|_H9RHK9qBnBm1%Pq>Cvs7Z@L1oj3URN@kzO|6gr20p z?y7Vx-@kA?-3A2Q)5r<7F|TWAC~2s#|M^rzX_G`EiQ;$6@mqX{D@1wmOevR^htc)* z^}|m^l-9bsI5r|BQ@Sw%KtvbZ-ofFt`G;#h5)L9AJ4Zivj#Lo0kR!%||sl8I7>_iT|lYCA;r8NejpZuv@dF&N@*imLJCzRFI$opyb z#QhyhzDW_V-R-UNe1An|Cb0GkPuXH%fC2}fnTwkT#G9yuWEEL08g`=IazqX7Og0%& zdr|D2oL#I9Ly4A z^r`JpJ7(D7O9&-0p1BrpP{7)1XNnZ3&*&qh%>?h18>22Sg}R(%>(GM6$e91VCu~w0 zm%1gFNO~*h@T~BTcJi!3V&T{KF~bkJ@>UNX9y|7`KRQP}U>2eStepg0v=fP4oGY(S zHVUu>|rqwJb z)GY1ETV)mWx5eK4(nf}1I0@M z#pB)KX;RP#R6;p2;HuL2E)Y9{I!zaB35!2T4T$GSSUC-9>088A6BjG9rShMxG~bgf z`$@|UNm6kk2RowC_QIWI3Vm-0gy|dTI!E;Ad5mwRcXnjguWFmSp5=O&r0~46wl{A! zhu`{Za9LDbLL$_ee9YV%W^#-#Mydmzf-E{`Ev#3N?k8=wA!n9m>Yc5BIqkM$Urk_| zw6}QonDIr9wXuB&s`qK z;@d*G>2!}zj>4UOBWlfP^m5Q($`$_hk0j<6oh$*|cXV_dw>toBicEGdFJm`nI(gWD z>Dlg-t2d%o50Hr4;%psv)1(Co<-*cE#@?z~N_yCDEBDoms##{vXj#o@C4W|yW=4#a zb$E;tl^(+$SoTZB*9JpE=JltKgYBgE%13d$fNRcCQT4vh)3Uag2gMIdFiB5}y~mp> z`Ur&L&^*bP9(%GwjHyDo2T6Y@m4wC1=GuyVHL68TGrcd)-mBUsmvFnZMf}AinWag; z3FOO2leV`^n=?w2PN`9t5PxQ(eqyi82w zD=)#t=6S(Mc5r}2DAqbSWzJTk6t(eaZ%IHJ(|B&i;2j~@L$}%LAZW@iG{AOa>)NBxZd0)Y0@#AhT3E3&uq9i%_lugPJfP{nPmlFI!*v?-Pdi0+yCHKY6Tt<|e1<#LIX4PD9y!rjUWy7A!N=Gs~S zz*LlqS$~+zVN@mDz3z}EE%unL?efU|k+0mO87-H@S*_$kIcN;=l;L72XoBF&&G=$c z$pwAF%jv~eWG^9}+jw&sw{(M#=;kTcU`cK%ZS&rDE(ZVKy zP8ok(>@zCqP0Tt1fowUlG#?su4L$l1J2A7n+h}e6BvZ-tTglaYej9%sb^v+ic9wQq*<%|%#&6@2U#xdCr08HdbO6#oGM zMTp}l4$QB3)^pGyCF|%Vh*k6Q(7}h|Qt7j0=}_6jIMfj#t6n{Ei7XMQwYu58E%BQmU!=P-BH`Wr*;yX zbm{F&X){$v1FG;bP=AU;h&2P_wTa6(-?D=+l$H&H=zv#UPL)i(NJcAY9$(Hp>vec$s@3I@zrp)CUz)%Xo z&LXDP!u?_*d-cdCUQU-ndYL@_nq+4Ty2r-vACb%#Q{YR5`-hj3W~p6zp?%pBMZA1&fDyi~c%S2e3=wVf5U zfZP#1)=}#<^s}q$Qa4;EK0bc=#}AtU?V*YK*L7GOAymH>tq3;r+~#f(z3I7`=ze|m z2kpBN#X71jdKptP;2gxR?`0w@iReEPA=>{myiGZ}t^GfJR;Dae>mD(sjwDsY_F{TA z*jZDygynhRH8X>@*Z24SexI+#!Io|*%b&IFA3MtGPoN&Gw%?qXn7HNTWeUOzqt@gS zPmXvYR-63f<6a?)5-aGZERd4s$jqjSr=OLj9ejp%B9#YBp)Gp!?-G<1D?fRvn&m^f z8IjdbG>8ekuv!f|qI7sC<0ODf4s;k|&bz$&*+$SHdPJ>7p^qS!Xl5;}V`6=%nlliU zZB?RFN(e&>ce-t@MxT67Z+dA%vN4~CC{m7u!)Z=XgzdX@Ghq`D@Jj*20o+O_h8nvn zf}~z~!Kgt}Al82n7G!po_4@hIRjS$m?b80$!^pu;W%O(j6YD|G+S$MN_Z{ss433+u z8+h0=oihH=-@bhtTPWul(!1yy63}4azZQgsB&nD|f7@r&Ig>|$Yut#%2W@P$7J5^~ zh(ihBmXj)#q@wDaXRPIjtC5p+Gb?CamZnnft%d8Y;jnntPS*y`{TAp0Df@FvBd0b} z-<}-oC~Ttt+DafLB?XsbRboI3Wn$211C=fp3#33DNv5muNW7fc{cWbjC-)caJ;z5< z4MF~~ieFB#d-29dy|b4CT^;C2w>ehyGs%ERlKK<~@(1lIQkUE6RH7%Y0C}^FO0hnc zB5*h@hqr_<*{seTTq6fd6@5guH7WG{_0rYY=f*TtkVxA4I)6{Z&q%fX_!@xb+kJ}rP!%N?%*g& zHo5Ijw>k1>utdNhdZNBXp-+B@VJ!5AaKYAgEbyi1}rvj zX;nZsc!+0hqL}T_kvl59&|k>vdgRr8YJ+wZ53|_&p|vnNj~4%;cW(6lg`<4kAy%_o zV*5*vJ%U`KahF|nZS9vIYOQD{GxE-07*DSuj&-!NI9BUbsL&^$jLD*DYX*sk(wuts zE@XY?1-!+(0U*!YdL_%Jnxij6kVi|x@W+-@j3Z-muK{+blAE!kA3bt|A|evquF!Yy z!s}C(B_po?8ZLLhlckq45T!;nSl?m&&1e3#P2ZTNeYnz)z&rSrUG~M3ncR@vpt1UL ziEm-5$y0~e!c;tR+c81b@Ryw9(2JGPDQGA{rbcv9U09{wU zJ!?zi3viBZ<8C}|`sB!wEo$O39g$mn;2XMnp}!VH+IVY1&A%eseo9i3^)Mph(6x4K zDPDKTb-=UMn6lF+8MSM4_(uu%mxC&BF~Hi1`gf${P?GsuGI7ke7z8MHHEDj@RTQjJ z0RaWjH~n0|H+Ja)x2VLm7n*Ucd~DR>gGrQRcvW}Cj!k(TC1txggQdI1>c$<$KV%;6 zorSP~!=XgzCWAKAytNg4w7WigPb0^9bE`rypceAh#qipuOZnF7$~jQu-?p!pBO1Q$ z@a}f>GL*pWFOM3y4k2p>j;E_SjH2uXFQ?7Lv6U5?K%a%Hfc)xp9?rce<~+K}8>Knr zb#?oQ-zsIZsbs&Ze699F+nDczbBZkBP{)W(Bn|H^b*q`L#99~;m+E*HmhJURL(;>u z;W5-nX3cB{#5@abr|+b=Pq%z|z^Hy5kueBre@`G}4|tmXYq$0F^%PT6(+2P%WNlUa zSgr-w9LWbp8wWu?tPf$g8gpI1kHH4Xwba*i61o1#QqN&@xz3!n=gn#Y+(L;M#4hmy3o68ef1D*y0p0$#` zd93TvIDpxD4dVYCC=yogN`TE|S)tl!p=aYC!%%I30{flVemo@&2)ayt+YLFq3NWME zSUy_eW#sZzPNa--SGx9(oFUIz&3_P6C>N!WIOCtoxanbkO0|p7&fKbm>~gBFRbK|Y z%LCz$U0`fqca8we6_u{9oB3aXVSo8s&+;t56*|L_OAm?NviP>mhU?+Jn)$p|2UV{x z9!}K%F|kgczr5-bUR9#dchl6=)HF0N)8KFo!m^`E;&J|?cqa2;b#$cUYUe7Bb)X6b zr_(;As1$d3Si(>URDxSlH7zt+TL}*kO`&66ltD>3_i3Z|aSe`16eKv{Ept1LBEr9m z6&mWPDUtX-T3;@gSeQ59*==fSs`H|E?jKWND8Vz-qk-+23Q( zrbbGx?lGBBk6;%rLamE#Oa?nA!V;Cce&!rNdYV9fVZ*v~-uY)2d1}vT>1iB(zf5E$ zP(zAXk3q~#zu_U+SF_&?9Y!wb(P>|egHH;5k_=rIa)>e2RB;wNp)r5CTF>I=&Cd^#rZC-n z#UwuFaN+&N5_O6mRgAecA}T zKUV9dYL=G$k5*bMC2$wY6`EMb8NsdziEkc5{px~*{rN0#g_lUcZ(y3hOjCdr`h)d# zm9ykm_x_QGJt;I#(*Cb&#-E|2YH}{)OqX47K!8-90j`2$O@J=c#CGc4GIIc`(EmQz)?%>9>P0c;#VDHYVmFx@G;*r07H43gG zCV^V7t^U@Rn) zu?z6?f2*MLgQV7V{VzgAIgL^|j9_V@ox>hZ(V_~cjG~qWWZR6uRY!;2msSiOkGyAn z`q54xjPouYDh@GRzttSp$t7wgRogq0mn}JuIMxKrsd}@ZyFo#V;jEI+;ie=%I{5Or-k=;A1`IrW8&&JF`pwzv8-C|*1HDh zjt2VMK4kqkD9!rO{6#xoTtY(qMep3D*eJ*@r_5TkbV0sexNK%-W{y_!*|ybHVG|YZ zF9BDO64vIJ>*{yHISX7~76h{wE9YO(eN?*zn!?@$j<#J$`?%k`N2^KuA|}=Tm+rUc z4tQ3UjQq8>7CsInq>>~6XZ!22y1B%Z|AE_eHvgR5qn72kdmslB$JlZ6eo&*+w=u`Kdvm2k{~<;WZy z<9V+lc1eVHslG3%#()+W zZwzQXZnz%XU;|PxKhRU7;^xe8Ubh=EY#vySR5ewUc-ZG%W)*us(4q+;F4i_Qob@L> zrvXeOuF1G_RSgYiAHgjDM=w5DY+@EE^ttxcOqSBa#_O@?;ug|v{A`I2x@#UYo<{XA zHKB>Y&A_fWz1?qaz|8PV?&b(~BC#IZM%MUfnPPmXW8Oad!9iPUABkfrig}iGX|j^9 z%8Olt7o^*QJdUr-y5dA{ci(o5HfU(Q=}ac=uiij!HF;<275$8}348kkXZNM)?;ksp zW7_}U3(y1xl(E^KLvqot2?YL90wK)1BP!EZ3^v(_JRTrHdGCMJeQCoVE;_3!vRHD| z@1Cz|Vtr^U-03$_-)%W^Lq|tvhQd0caQ-zY)A`Me><@5Ki~37S|<>`70M-V=u*lFcS|bhvBlo^2DS6A z$S%qjeuV7CZ&i5Ba^!syx>+EX2nP5!bSZDr^?APpO#Ot$#lZwHOWJjyNcC1u;6S-j z(3koVwe~_DAn(WP(cAf3>!**xaO8bjXj?ZtN7&^14Rr9CSRKwa0s3{_GhNSKnpmqP zRhyyF;h}k}=^lH<1cLX7nj+0kyTwn8bLw`i_Yd)}x6>MB;U<1&?>?P?L@Yyd?&<9_XC!fte)JrT3gg-_g zzIMaI+g5ilch6T$6I)Pg4mXJLf`$gc)t+!z)az({0S3tRB=yz6!tn zOP>Y9ICIfg{~Nk`?;hHS|WtA3tm1=Gw8P1Qpwr z)m2K*uepwW0s0Bu|Mvfml*pS24#Lk_8uU%oD1=yED)=ZGT|wVpyrBN7rfZag)EnQl zE@=+4!!i*nIOwqnG1(^Km?&H|hIk z7tiHBt)N>ifqIJ-Jr~Uiu8woFZM3B`KHMs#A!J) zm!u+Fv=kN$@(ZTQD2={Mr!H%d6e5{}!5eVC&m~QL@ zKM7~IW6LgA#0K2RWy}_PZ_}Hm{U;v$sK?ey!S5VzK!Zv-!o7rdEQRo5he3Z>iL(d! z?_{0!C~B6YY6F4JWKQhFDfuGLq-s*{fq;+QjyZcv#GZ|6*Lb1aMm<(wqJFB99(F5* zd%_sSZ(Gg{*J-r)4cX^tO99(So0b{en{R2iF%KHl>4N)Y%WCQmA~3!-XoQY#wNw#t(f*a^@1hz-AU>$Wzg>^Le{Tl)6A=XjycP4<=$OX7hX(6AZOTg}us z9eQt}P0hXfxGyJ3*qb0Uy0`cC4+n^S#vk_) zwUJ`)yDDDz7j6YXm*w<1!*339P|w9s6psdyDDA%RzvZ7LksjUu6t{WH5%D@~vnzVE z+J61rN?3N@c@&-sb>&lHZ>B-Gbt@+$la&Y8V__B|!969+qEg19jbG-#qBSi6vASOk#IDa-wp3SoPSms07I1-gDS#ucW%t z(o*sTDyE8g(xgIY04o#MtOwc2j4WW|JSACVM zD(K_8EYsqgfL{143R*-fUFEpV5o-~7JZkLVapkVmrwxoRd_j7b)2dGiS#&$8E~&Z@ zJAoP8Q=zp~b+prcchv1zkMKMFvh&Qi`8Q7n^!oab#Ikmt>^1a6VBMING}sqIsOCGc zOMBE57s6pM^t;+Xbw|XOp=Ba83_J`&~`ShkMp=8*(AoXf2-q zNXqV{>MR?=>9~a0SV-`Tpo!H_A4AGpWB^KwAZ=(Rw$S}l>PtW>S24WVpf<{F+Q)&D zlT*G=RpnI5ImFlWps30E<)k1N9>gW{55ybfabJ8Xxa%?CdtvPdEm6J(x9RCR+E+UKOY`!JH3RSv#w54uO ztv?rf8r013_UXzq+@tvZ6Ntu|+5T$RZq{lX>)s$dCxiBxJGY;HUMSik1e!{0n|LGJqd24H#k{urpLf*?~FbRn+w?9427=d9j#q zM|?AAC{K@m${?ygBQui+`R6Nq2xUI@z&Z03N{TGepbo;AvfJHjNL+Rh#!u{yaGbI# zWX<<)aDn8C-lf<4&J{8L@Fe}7Y}|W#(+NjM$JoPm{ij#?3b0qYPQ)vG@qXU(o%YM4>{FL`OMxD6d!{npLKuFu(ycnXW>=Epf&E3;+a&a*1 zYQVL2xt?H%6?lVu91`#c_pyi7u;GeizndJ@q3y3tW2fno9YmmSY@vaQg_4nv|P zUxm9nnvDH7Q}6LpKDRjD#&n;v6^^$6{CM#>Q}9xQ|ELCFJmghFi9SBmzv+WD^x0eJ zie)g(vJsS2ZR_I{vOkYM~(FL0MGGWDt6dC-nTt%L?6CwqbfDHpO8};-1I_nXv zP(xCmmPPF~GXTy9xBB^scGy|@mu;%wJt!`}qf#$hoMPS!ei-2%8O zsvbOeusJ_pJeRAPRQ&@!Y;NBBF8%cfPD40m*dZK>n1RoKrK&bsvE##PMz&;*qJV4W zV`fN4#6@IWPaTQ9tC1bh&bq>}$BLphe0R|a+&c&Cj<<~e?ekNC{spxQ>@w6!UDjN* zccwY;*s>0d82jjvvf<<>Lc#$TMwnKmZS>qT%2`*^5qMj3dnn=_;$pA1movGyg8p&! zK!cy^2~p56rZHW2-<&rFe(fQuLwjy5s3WSFIC3zFrBsyu307!hUOnbZ!eX(1|7i$S z95*5DddyG!fGe1&>50SkxRuim_LA>H8XbNQo=yvx5@>iY7Z`Dsjw>F7Bv=B^MKj$3 zj?Q<;I669pY2*aYuD?PS$q$B}kCS_s{tSl7u5r`krz#RRTZOH-TuKpg7oM7wF35Kr!UWY@uQzBv*`QO-U5EKVoH;?_S2D z1Uq+Yd)YEWoNu0vtC(W7dr@&FQmD}qgU(xbzBdSuRMHWS@?_?<5B z<5m2cE6!Oe6sWQro!tUtIfjRZ?IDpLw1Bob$X^bie01IN$UxyeP}X0SLZCMZv2cy+ znfuNY*4OA=Lk(1xME~2Pb3C_{7fwXXe}#?60*9~n`f>gh?o&=9FH=gVX<7iL-*bc; zaj{dI)~ext1b4ol_mqh^k8kbpv)s!P^B%{I2}}L=jCZjfo0Y!daKzG)XF1DWbvtFx z`T_KQV_J4^95g^LuXv?|XSCypJ&J-&L7gb_UyjSjtK5s@^;kIO87wB)*{eH2=^n%> z$>ke6F*7yP7WB(M%H~octOoznnf#%G9+#dLI4D*tiYc3zm>4l;vTQE;coFqf3s|J* zM-~mR!>5;t9On^v3hTvS)_(#FXrat5O19OIO` z{ggP}$G(uS!Smafz=en-ew6<)ZyX4`hoNMEpK-7gCf1twS)V>`H562v=vVwIK28E#s63fYI!1}v^; zqPm~Ov9nQ&Lx)eZ13(?AX(O_ITQ@}jM6>{P@!N+2jFu*LR^k(`2&<0TC`!}uGpu%1 z@-{G-Tz`rbQ&(44ge;OkC18@o%7fVf5hU>A44x+^ZpLD=$E1yY#RD*ezWEaoSahSb zZXS-=8_=Zif`}ok#9dbQotaO2VXOVbc3w|SDV(+Fxxy|+U>S+ok{taYUr{kCo^+5U6Iy#fo!seJ>~We6 zOIdjAM%A`J7G)RzeV5)szu>3*p!npl+q!N`TLX_9Yie18uDJX7B;qF`P7C$k2s)Za zQ@1hy9qQ8X>612$*!-s*OZU*(`x^3fU&#jaRMz}D{4V%J#Xl7!#JNPd3yk=AI__?H zFsAB3@u^*L@S46F2ek?0H5Ye=u8w;D1s;?XxO@;L%4Q_dt-^?8jo$@Uu5lDlJjRm4 z9>nWT*DW-Lx7x3Utxg91_6f7y+TU;5j%;V>raW@DJBH$>?D!;APhfkWffTY2$2{#XqM?$DK%jQa5!|uA5IN^M+=4 zkR2caoX&V=dePTvW{9Pr(n&=x9jJ-28n1RpBx6968>8?-+8vIW4?SfHH3P0e-@Ldb zMZ1D0Pe|r+d~Nmj3oZj=^w78<xb*OeGQ;h z{URsORVU|e_bEb85wvR^NCoY6RE`0EiPuD0(JrV zC!t%4T=$(hQP5PI#ioika*4nB^H|NnL;e%z`!erR1G;Y*<^zxG+>V>Li>=^cpRFhk zY)ppmK{yiMRz{@lvd*%(nHfS=ChPi;z(nUC^YYwPLvbS3#b~vo4!2g*Sff?TRwXry z|GoTMiAKQpH3AE{2Kn|G(8s|=aKrZJvQ4Jsl{{sM7<{Ik2!MMY)*kTNV)`Rb$Etj^ zyYu4CN`}2qBlE#@!xW#?+Jla7RVT`76s}G;|Dec#&dT8sE|uoMmnM5Dr*Ai#W90!Q zsa7LU1lU5Y?Ehjfk#j}A?{il!=ipo`CM0z4HbVrCzimhdHcd342sS8!2#=-laWmw;l)XAWQo@EQLO5uwfJlL@Xj_lXH{A?&2;dz{K zFsV4+5c(|`l(DAC0VN48>O@RcZ21q=)rJQwOSULfC=5;;u@M?5-i_-rb{DrIMjI{@ z1px$G1jRH^+&IJIi?a?Zq*NUClLLBq3QrUSG|5gnbDE6+txTa0z$7Vfxg|ZPbLR>< ze!AiNSM@x83*F3w-0et#q4H=5%8m+xElc3(KWip$$m$d~RTn(GlMZlb5|2ExbPX`P z*5=Pw4}MX3mAeA?xfDK^M!ABuQmj7;-8X#vTp8744Bc}k?>Uoyx3=nSPK8kTK20%> zs2qU&D}llH5`v9=RFU~6i=gEr1mcS-y?M3G_}sa3mywS^#OvmJaEv5kZs7Oh!632@ zErej8#xcBQ=V2`lc$Xhl(7|1Hzv{8qgdedOLk0rY9SmIM58Xp{V2*>gjCOa*gw?K2 z9U3Uba$m4xvD0b!xGr;KJ3*e1Ug%6_ckq$S{T>mt>%AaKUr`+rY@A;mJ}xL(L0}t zB&f_wNJ?^|65eAcF8;HaL;i!ZX(NrMu4itj`i-J*0bas}IQb_w^4H8eZN8-kogxQY z7Nt*WxRtRxX zwkYYPjaPEAPWj#qfy$%3r#J8Nte7X7Fs6oJc=w{qJ`y3$BFLi44va(|swf1Lo57^0A*l3dush%{K!Ra%S8f5_N6zqdNG6?+Tt`y?M<`#1Xq{C_%Z ze5}-3q1;BH+(wOpF`Z7o1vazSmZIF{Uq=i1+0wpZYwO#q&*hecfPxFsPn#o? z3!x3zr$y-rLHd)uPp9yc_*)|L4A9gE?d@HK{VSE~s zBYdIU=l|tA#2+J6>olnR3+nPj8N7fE5V>8?=4Nm5DV7T>0U*Bed>L-gNyfC))TsUh zw;b&Bx##ig#O<}TNEmbMd96t9aQRW3CMp6U=8eswDFQAN6B7#y3sCR!Kp2|~{IPz~ z&z(rhf9D&*y@ zu}2Sh=z;&i`Gm>a;orO5<22%94A?R$pT2d|BQ8Z!x*dEd4Bf@hQ!;UEaWKkGWYRo+ zZU@Wp2+CQyWQ+lTr&s-+utyu-CcFRx3CB=R(p4)6^6->yV@2Yh8;0%*EFWO^xK#;_ zl%JJ9nX)gL`~FQkYZXP;Sn*uPSq$*Ht0R z-wd)`+2S3=t-=Hy9UbNRREHS4gmiO2${5-SeIo=mgnm*7&RdDRR{V4iR;Ba@o~u_+ zNLr0{Vy7NN#i>~74Lw!q62>gGKU@b3_~N{eR|znp_8$1!;_Mc%5~m798r7PCH9;WC z!R;u%howUr9OO4o`}zfbeTZ>uh#?p^Y{HG-5hgx*W_#X|tMM{Ot`q=((7brPp)+l( zBT~@QsLSGPprZ6JF)^__;o;%eKq5j|9!45f57-q2n4Z8#FJP1?BM=&KdhKamS|}I5 z!c=&WoxZ^K+AjjBHi_M{)vE_lhR`R9c~^vC6E47@DBh&Hi6^Ju$rkiW%qPV^&pjYW z?bzPGG?ZMC8h4D;c4Yp3+tmn4JIRnLQFbn%D2Sbrgn!ML&d(|Ny(DgQ|EYfXvK=!Y zpmG`mm6lRfZAEMk&Oz{Pt1qOWpOtyfn0sG@zR9EQ%}cwvf*LJt_u-?G=D^AxHr&nR zbX+>pjBSVkLwr5+FZ_Z+TEioC#j&)vf{m9~!c@pJu>OhDwuyf>AwL^2EHYhVDM4DYyATloZ)O zZV#hh)-p3QRd}^2ec*5NgHus^E8mv}Qw@0;9jABClt#CO z+wI(D32+=LNvzcx?YsIZDk;tWy}xK`EgY7Yx!;yAdgW2^+_WIRv~5YnXTovD21`0W zB4DU}-f$EHe+2uXA>`%+^f{b)3qIWif0I5#PlQZ%gyBP;6UAnYSt7t8j>E~%iP$=( ziFG~zo%PwGz%5u1;IL$`%phk-i@tXhk_J>TvL+`dFIrewM8VjO6{*R@CGi%6DLbt} zb1YPZOFU1&sV}+D;l+w@S;lcrqtq-3B}K*O_1O4%sZ35$t#$?zAuw^N42-t~?AYT=*i_ha!iMVAurcTJ@PB8>uJJv6j(jEQetp_P}zwK%jACM~# zWF{%m3oSNp4>}2ierrb(;AqBk@ zm)gH`lmMHV-E(+%q8$AGx9@SO2sw}fH=D+afQlbblLb(Aj8T`(p`wU5)uI~@rvmXh z*?e|v7sF!@d<|kp^uam68ZaN9a7k9ft%QGKFZSutV6n*a4my;L#cNX;DsP5!hKXL9IZ12Vdg_u7#jms@J zl!ax4buM`biNHHr8Vr|U(K3)sKw&{~O>wte@DEtP{TUmK1zlwPe8htuoM>*sJZnt8&h%z8CN#J3))6M1GbQAyP zXj%SDaYL1|r{w+p3nx%8$_@tA)3bBr@wU*IvU7EVWwk%l6{`Sh6vbOi@vyiBUf(}W zJP*qK3Bh{{%->-1G=eDzp)z!bEVOBx>$BYhA(Y6o@$rAg6NuP}c|oIvrJhj#<9Ny$ zAjHm_zQi$@2MG)}<|nondyL?$g{^^}2#RZQ`gwMv)X+SUusoX1AKVc>U}lzV0pI17Lf(&{4Bf{%a8L1QkP+w0?)PEO?5prJ{;$qsw2*m?2OYvDNx z37}0$IZY0?=xKkIDbAzTqd%?OzN}l*`BWhwn^((EJ3gpG_cKg59EI}vq?h) zIPn$aX|>i}45T5A z?CV)!l}5N18~HXT-I9b*3F_IiQqWtd1V!MXu72|W_X6De)9wU&(R`1RP$~y#N}#p< z@Dkp~L3kQ3_ZG4UQKZK6C7|>Fm2@WFQ2*Z_e-DGnIwB^-h{{A6rI3AJB2o4*5|W+l z#yU~4ldFKtb#>nb}Ffm90C#(Pb zb(YAvd8g3uU~|{S@IYqFzS{)!xkemQ;yS`qf*2%hc%4(e-wjr#3kCZN=;Sg(s#(95 zcPlzu&x2Y!{`&gw$%>Okv4E62$A+p=~(CwuxM-Y_Tvn_%!^Lp>C3T z5RNeIu~Vm&v(2JPvF1U*XPB2Rw949y!UvMog=X)AbjGz0-Q9zPRA0$Q&Nc_xX}+fR zftCe|yGOq^|7qqHNFl_HMS`?R8%$`|?M?48hkEC~P?R z0G2F}BkRxwE5}-^2xz{GcbiaO5)3eTUwJ;B`L6u^KGc>Y5SXtLM>T@5LnC`EpCE}^BP-?9lE+%Hha=HD%cHbw z+n)A44Ag$H?32zokUak zUUZF`)>bcZMiixdzbzz2T$jtF{)R)y2wcA|R8~chSK>CIPD^Mh?)c#?voqMCSy@x^ zkg{_IHNpVFmjn+SiyO_2tER?EsVyFKc$(s~*aE#e#wr@A90`!Xqy#JBGeHuH#Gp@- z$~FA=AXffE&8QJ-sU;X|GxK%J$u)B3^s6mC zI`JmpZ}U2oo!?DY2x82!=LY*H6EWxzo?60%r`NLSBr2UH6>E3es-SLydKeN_+I=qo zo#WsxZrqle1W`Z^*Y5@^I>zi8L+rVy0Ly!=yNfM*(qMSBKjCR58&vNbA19|SRBSR% zlefqC>_|OU)^zW`uerJ$yh|<)EN$j`mP?MTTYec84w1yT?a8H1$a&N9CM8 zDL;Aeog}ZwNiFoz*%L&hc4pMLm+TTJSXZYTX+ERsVln(sCX>pSyJ^mK0$Fw7c~n`! z{ek?R7U|;#(@LdA^t6JWXdcl2V@~K+ULm1ht#eg_m7hUwRRB}BF_T=VN+ub zEp^Mx@At4{epyzIY$VoW^a*#o_=~{H*@Qa(uFD~Jj}SQTJP#iuhPK)8!z-pazsi^z z^tU=>PL^2`Vk~QrKNVd&&I%QbuVnMJAiR-|XIFg%2{|jGgV>%?;}uu$CZ7<9_)aRExBZ z1n~9+x0ZQdXI{Be;3TG@B(=c@Xka35efx4Ku^cH0)eD3r>*mOM=Xq3G3~M|X+YzM2 zxFW8&r#GpqtM7bSY`Vnwc^PG2{Efj53^XX1(0neK=2(;hbf*`k_zyyIr@Z7sNB2B*2DP1$+oh*9MRw3|IphT zkw%R`D~tJwvDsVX$R6uK*5Tu7sR0X93mK6COTIN8-Q-Xxq2wC@tq+JJ=~l(@-uiw{ z$tO=h&zLs+YF^Rr8xc*fM-S}3U1~d@dV0NF`^dD5kM||a1Z6M9b7=H6M=tgFXSB&0G;r#uulc8 zYWf-|dntVeJCIRwA1nKn-E85xl`#5*;h@a$+1h}kiTpV6n=RzHs0lKw?SzgiheUVG z?cV`&Yy)pELiMH&w77e+F>WtB9h&nzMyU$@_<@GyG$gNGqr!C{e*E@ub^;p*wo zEJ$2eEu--eqGPDwY>z(i6u!B+-LwnB-4Gj%t zw6i50hY{j#4NaOb|3QAw9r83yhLw4GkadRI9R%0a)&ccE{imz~S%7k)?#g%K++CNK z6d^mHF#h!gezW0+-v3YH)f$x!K9R3$Ym1^fBqV@2*Ib`(xjH&JhP`D`N(b-DSPa)A z7!JaFA@v(=DbIY~GY;&*KwwzJ8yisV4=w629E0@}JDx0Unj>~68FeuHew$Ktl(AGVY&_gjEHbK*y%u?1`SYfSl3H^!O{kA zuoaJ>-*?gCh>rSWSM|+N0L!NHD0~u4+s9iPms;g{j50PhHmHO5xBlZEikpmQ*{O;v zcW!(r9@7ExzzoqBVr;ONyTWrlx@(2(b2T~=#5d5prjT_Lh}DDY5J0ZI#;1uK?B!*w ziDqE6YCNt?0#~){KudUbz6`U{`R$guMZ(3?|G;=s6y;$aL4+SS$ zR}^VPmK145mvyq3(O0qwby{cka%H6*@;;XlXQ{HLu_}^TohLJlvWV1mv;4AkHLEyL z(c15^>?$vI4(yfUwT|6MUGTMqL)cX0*8-Kr(63~_wwUG|WQ`dZi^DZ>$d zD7gCBP&DQ&$(siQk;ebvzUR=aCYBMaB9_67)sT#9Y-nhx$KzwI&mSr|rV7X+?T-v~ zI|Dk8@-m6k^>5$4u_ph1yg&?c@`(Ut=$B>vF6aR<}-b* zud%j8v@0noDcrHNlBw+Dyfd9u^XD)T*<}kVrGxVV)b%}w{IaZ4Ik6NsIhZ>JnkcbE zui2jLytowi3}}@*vG@70cJgePoBsoly^n=b-t@bPzO*{r4Ubv=c=XX>q28iO= zp0dXaiK0Fo4=U$uB0nWZS@<}EblzakKeKMaHvD$1q|~~SKtK&QTOTKXMZ0#lHiF^j z?{7W1t3SC*$&npRr|w1Od5{_v4x*IBstv7#k4cXV?MKHeksX(8yw{wBjs(8bYQHmF zMFGZpf|i08o8IOZP&^wzc67&N8$;@wxXYSuV66q7S%*?WL&useY-~>HnV|R9bUR~@ zqJnIouyI1i32mP-a*^M9sIXz2$?(=AhqX;)CRLSJzfX;bfgT(nFxl$2Dqts9r?Exa8sgQ?R*-S$}tcUDSZCqXXm@?(!Hkt>}7K7eN z+wQywRGD$S6T~Ag5-d27z)dINUyiwO2pjHK>pzRAS64;laLNE-eO<}d-~VEBOUtZ{ zE-H|H*Z45^>%HUPE{a1QT3sxI`NCr~ZSekwlf-pZNFpk-hK4ijES6YeO}ACCLfL^R zj;GRk9PR^I4B2;1@o%UeDv&+gXixMBt3aV2 z|D$0!%OMY4P_{q`lCWGBan*z1UpLaDZ>=2+>P($d_3|Q|_E4w=(?BkZeTHIL% zy=$Mzqy#r+`^30TMCtekOF!r(eGg&tY|IXV^loZ^0eeUnN-ZsLy7dNx7v9>`&APi@ z?%gJ0XeA6zP#YX;MlcI4FL|X4;Wxy%b8P@a2SitVnA9@)(Fy(vM zfCseOohdX0krn{7ctCoPz=1b%+!6a59{8rFGa#Ln<>h%0IBAVEk?vSZ?_~WQ8yj=S z(^HCQ)n`mM3};lXyol9%hqH<l_{`_-V;73@uCfe5VbhQ&_WM-3?S?9OzOW(L<~Ayxvv~mRJP!mUzUsHRXpCQUeM%az^X0UODj0bEZ~f{ zxiOW?9)FpiurKKu<^=D9UA+m6d1Whb^}y1~yb}lS|0j==)0-D^uNoO0{YY*jIHz=3 zOc<@q9Rx{mIW7eW8)kj4!Fy5H_xA5AM?K)*kl@mVwq58j9|-V^1C={PAIz}8dj8TqU2 z7C_zUhw>HoAMHI0F}&gSGfcF!i1z4jgMg4BWFI1(pd^y6R76v&T*^O<(UO#qP$(Pr z4i#Et>$J6}@I7)O%W?cuap9pEK%f;G7j8DGW~%fcVI8vf>MFMW~-NBQdu*cW+^UQ778)3i4TnKWYByh3vVhenVQ zD#0^zuU!(jwz&_M{*OK0O9d*8S$?3m99iUbL$<7O^m{p$vriL)$=)?KHcoM-%nmPn zz%TeuW%BHVKVig-=BWtl-3yY?R<3R@#(6UoGO6W35&oKExpH6!?~iGLPvtQU+(FGeXw&FOcCuf zc4%*YUhXVr`P8XX7X1yMp82C-&yCU22A9M#&gw#a-+D7Fa%3$V6{7FvM``f(1PX%_ zpn5m;9((O>?hdRr@@X>JY2}RZQo8@AJrJOAYp;#3RO$#v1(XY&b?%YGbbxrJK9p1X zr~^n3LWFS@(Kul4Ze_<1(!Dj8f5P0I^Kh{n|2<(IOtQ307V`|zq)j>LURoj1O1Rp? zVaFy%mei;a?H#`9;jnX0SxnPx_@Qv}n+rRU9iEbWg_`S(b25i-_)|wN=Br{2@|P|Iqcta zK@!?Z#8A!0)CgfiE0kk+d-VRnt)Vcx;`V0Ng~EZ&-ExNiXU*UO37|@jZRb_Irq24k zKR12Qm(cg^AY#ynKE6P+_izl@ldOYEM`NHK>5=J5YZ?L==vu#GC!v_Rb#CO(pG1{_ zzr4Zi4&B7Q{lTWDE7J$Dm&7v2Gpbro7^Q>rS=9AC!#od5i(jM}RsKiN@uEM}{|;7_ zc2feIhgg>OnxtC)+`WKY_Q20{JmF*R!B~F0^2Ilt@to4i$;0knAi#)-dl4=2F?42d zc1?|zKtB+qX`IkYpWEDjS5fg+p+DTl2ECyXaP`%V{{5JXEzQk;R095vx2YV~9Fxjx z-(4GP3HTa5i6+jdiUdgzG1*U`Gf$y+FK1HExxw13wD}U@!g|OMDU_E-amVv-{48Q7c=&aM(sLB9whaOhIl6N4pm{kX3_i^OyGhd^R3UzslhH(#An>XW05(+Z9C z9veWEzHMsA_XleflXj0ubD6#GDf1Q~6>BRNYoY&qLLYygKpQYv&~Ss}q$#{jV{hr=q(@kl2kd2=;Lji2w< z52(c<3x4b%`{;5wss09yilwOa!~M-~WpH*#&5b=hNPxS05-46I zzU9Q+Zo&A5_}vp=-&Na_ z>rN0Xum%M#sXD8f*nR|=y(g-bH#ce)hK&H=LjJWP7Y$(u;jPdPdsb*4Btb{GPkrMlOseL>>WI!>MQ8TNJ z8P-=He;3fQd2vd_Vsj!C01BPur`X;IBs_Y5(ib4~z6H-*QX#XWZ56#yj!6afSZum%Gp3BeU><_R}_GO*UKu|s}ii)G$_Dk}0D z6#8S-GDXrp6Gwe_-0JO@Nwm$Q28G>C4c5=l3F7CwL7(gug3Y|QS0~!f_BZUTt=&9Y z35Xj`lHc3d4T_Ey_i#uS5D@rUYMga{SUFbM&`sD7fqQrw6CsDGE15)|TmMJLaq2>! zc$8Sadf(As!h9;YUnI!t{6WRPXSeUC&mDx%pbf$U=^q~fO5K_1KcxVI9VajegKI3d zgMhW~zog}Gua~Y(G*{0^U}+QN%DI!ABu03yHG-vO^>H4?VNO%#woj{XPk5wh?$&3 zempV~HWVn4%Qvu6zI3pS9#C^nkHkR35p-G!la#r(%L`@XxlT0g@=grS36c3C>U@u% zPXBVwXsdvetvHn3aVZ4;u3`}kHnftMjnul3I-_qTOiwKtQS9H)x3Q60YSU8a54IKl ztY_u`=vY9!W10K3)u|~RKR>?-FIeGY=8poE2sHXsfWLo~-~-d^xysoIvFn zkJqdzd{9k_*YT-tuNk%&e%Dsh-d5w`kZ0qNmsTQ|nO5>5&m$6r3WVb{v`;uavV7!`P93z+&GWIm*f!&+-Y5&?IKTV{<&UZO749Ok~H z3$5bPUDJWoOM<&(A&Ivkg2$v|2Gpp9(ai*}z9(xx&Ps}J(~6%kj71HO+D{_rx{B9$ zKoq`S(bfb$z&>U6;hRwQbKVJ5$cIpHsB^f*@RtUK%8H7O|M!>^F$}U7{`^5MHOs~Z z{jOqB=bts%hKZS)o!PAQw+4m&eCt2T7Q^|)<@^Eyzbox(w@v}wPo~1d!>#f>MkMl| zAaHNJ#m-}<(v=dF_)ogmL+|#&3ij}ws|U;^IE0)&b$N34vb4d>L985R4S`D!S#>-T zn%-T;XE2C$g*}s<@I_Lzm220jaL@T7<d9 zx;_xe4y>%N2R$$IZfR*Lw*#in>P_Ec)ywY@v!qI?RCh&-TLOTIGewzK|9_LEkBVr9 z*rDFtBSClH?`p*_M)S7ekh6M^|FycIr?=3q$iA39Sm@ja2_kjl`MP6!vHC}R)1kJy zAwF7kn=S+3d-+B_Ov!KFpCF8w@{F8^j{JJ<=QPpmcMj)^!#30`wN+Gns;G#xu`%{= zkj(Rlw72+gj0dWxBrS`> zW|{{DEFUoKAy1Du<;1@B9wEFxKq1*tUwbUWw77tJ0ju znjc(xIX^Ms3;Okge=aR8F@9Iti2+l!aYvQRCO*9n^%`HrJX2@U7=pA!3V{1XB<12L?cC<&$YOV39^>b=W5_6y29lUo**U?7-1CK1a zOxkSqZtFIGE1*eA`(E_i%LR;>Ixv>5k9)SSb1duqqL+ySqP<3)z`w}$bzLumqe2otx2Sh-m z3&dI%K=-SkSqT1V_A8jpwiy29Hae_Edx9U?qG9 zVfYB@0tMSc5-cPKo>}W5PUPzqq0%|Zh}};{PP#u8jQRo-^5kiLjSV(KG|GoneO9?J zPH5Wie{Smo%$w)(xBnUDX5|Vpqh$`m=crjXiUL&DTCYda1B9&}{x}Z+oOb_q1W06- zJH8aDlZ5y_So=wsdUH$4M8X(Ffkya8q3lH2;zK#kJ#F(Ft692pR=L4%tbz;k$vgj3 z&C(EnN`N}vuMWZ8wz08s%EO)SZ^*U2T10z>$-V=zN_Mjs?|N4v2LP|V^PefQ;e-Jo z5e{s&M@JTIHYga5HM>zl6P1h}L5rYl2Y5?!I_9R&DYtZ)z86!4JZ9b|2)^jV-Bb0q zNShGp|9Txp5+#Aw%AGp}bdg=(-eR>#X0!}rH}iM+BSm`SgcPtg3tzL1CzNq6aRMT2 zKAAF)PeFgWyqc0dMsMRk2_sy(brTJLh=!XWJcgB_Q z=S&GbE`1xSC!+V51NVXK*kUm}g8Q)(uDY2LT@=m+8devS#+dxiy6v6cn2FD+h$12e zT0}o6`nr2kL0ypp@+MxX4}lxMz0645z0)P`6EF}8`Y-bDONUIC*h$syVs$}QsSI;> z?qd@JJsqC3#swVaGXOjQeksUo3*ubLl&O2KiyBVC%j-ZA$YP^+3!MGl{jlf2lzCs< z{50rz9f$qp_^)VdZFUBCJJeYocRLAsUa616y-EjSS{>>ILZxC+5?{X}(L!CD8lpfE zIo}k)j=TN3Nd)HpO^ukY^c~i7Z-JuL_d*);Vxfoj@`p+#I6(dKs7(~A$h|eF$-<|l)5r&@E2F{ONR!3M?#kj8Zvp7GJ@smGs!;aycj>n~M zcQ0t*PcVN8vW$syq#qQQd)q1EXn+vM2M!~{1MhT=-0|UL69LPV-+;Cn>WgyT$Pe_y z52XHX1K&C#^GgtbnR{->_1;|$nc)Vo&gH1r!3dx*|B8BVX1iLZeh zyO#z39_2>u{7uC>2(EY;joa?sl3`fRH4NedD;WROyK@Y(yd|wtl9?`Vk}*%@2P;5) zU%`<#y2~KHOof(D%Zz>5l69`rnb+PiSoyoxMw1&rck%$DVE^9Lqm?fwS`N^wr9Mli zy2fn%S3mL};rXt1Y6oMqu4Ea{P0ktZHP1T5T?>FRf%wT2;3@lv7Sxwl!qP#gY&FW4 z_d*79K8Cds_v6lTHVSkZ*;Q|=$!(qL@gQmM#=64)dq#?$^a_dKkqpY>}{C8k`9Z?B9U?(Y)LfS>%*8P*bdF{u& zJHrA`3Nuc3Ktzg<*ez=QrhGYbmm=@mk+Cr(7`tj8Bft8R9XL=^sjZDFq1$bxh(`j1 zj9`wdhJmkirwo{1Kst+Wx!&ZSZhg;2$8q0C)%!pzva)dYNPf) zMGtMl86Y5BgmcwAk%KK%@&E-Xq~SLMRzGX8Cx$Eu*gkgM1;@Qy!^xoRzW?T4qT4P_ z^#7pQ*{r1$gaCwB$q7y>1kqOTA0d?`=kdt5&w=;C0aH@QLf7{1Z~`VR)(O z->F;fFgxWhYsc^G{}k0Ik(6NQ1j?u`=q6S{H5W$XRQTd~06=sk?zj9#lcrmX{2hIY zPbWOus&^ zJ9+ZZQRnrE_kA=^K#O*B>kP$%O{!N5K*3lZa#hDSXj;tU>#k=VkL0x_`+#`2JAyD< zbb2g}6#!!gl7rF!rF6GK@ra0H$x7)Upa<56getMouseX(); int mouseY = application->getMouseY(); + + //lazily load crosshair texture + if (_crosshairTexture == 0) { + _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); + } + glEnable(GL_TEXTURE_2D); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + + + + if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 10; const float pointerHeight = 10; - const float crossPad = 4; _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); @@ -289,25 +304,22 @@ void ApplicationOverlay::renderPointers() { glBegin(GL_QUADS); - glColor3f(1, 0, 0); + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); glEnd(); } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } + + glDisable(GL_TEXTURE_2D); + } void ApplicationOverlay::renderControllerPointer() { @@ -349,13 +361,12 @@ void ApplicationOverlay::renderControllerPointer() { float pointerWidth = 40; float pointerHeight = 40; - float crossPad = 16; + //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - pointerWidth /= 4; - pointerHeight /= 4; - crossPad /= 4; + pointerWidth /= 2; + pointerHeight /= 2; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; @@ -368,19 +379,12 @@ void ApplicationOverlay::renderControllerPointer() { glBegin(GL_QUADS); - glColor3f(0.0f, 0.0f, 1.0f); + glColor3f(0.0f, 198.0f/255.0f, 244.0f/255.0f); - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); glEnd(); } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b6066099fa..dc1d3d641f 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -59,6 +59,8 @@ private: int _mouseX[2]; int _mouseY[2]; int _numMagnifiers; + + GLuint _crosshairTexture; }; #endif // hifi_ApplicationOverlay_h \ No newline at end of file From cb1669653d771c510667caef7ee899d4640a148f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 15:05:23 -0700 Subject: [PATCH 72/99] Mouse reticle bigger in oculus --- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2b98360b02..9222cb092d 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -292,8 +292,8 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 10; - const float pointerHeight = 10; + const float pointerWidth = 20; + const float pointerHeight = 20; _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); From 587c0e5a9d1ca50b661b8ee310b8186a895d082c Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:01:59 -0700 Subject: [PATCH 73/99] Render Oculus pointers separate from texture for better quality --- interface/src/ui/ApplicationOverlay.cpp | 108 +++++++++++++++++++----- interface/src/ui/ApplicationOverlay.h | 3 +- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9222cb092d..fe6baadf66 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -37,8 +37,6 @@ ApplicationOverlay::ApplicationOverlay() : _uiType(HEMISPHERE), _crosshairTexture(0) { - - } ApplicationOverlay::~ApplicationOverlay() { @@ -259,6 +257,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnd(); } + renderControllerPointersOculus(); + glPopMatrix(); glDepthMask(GL_TRUE); @@ -289,8 +289,6 @@ void ApplicationOverlay::renderPointers() { glBindTexture(GL_TEXTURE_2D, _crosshairTexture); - - if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 20; const float pointerHeight = 20; @@ -299,30 +297,36 @@ void ApplicationOverlay::renderPointers() { _mouseX[0] = application->getMouseX(); _mouseY[0] = application->getMouseY(); - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; + //If we are in oculus, render reticle later - glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + if (!OculusManager::isConnected()) { - //Horizontal crosshair - glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; - glEnd(); + glBegin(GL_QUADS); + + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + //Horizontal crosshair + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + + glEnd(); + } } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer - renderControllerPointer(); + renderControllerPointers(); } - + glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } -void ApplicationOverlay::renderControllerPointer() { +void ApplicationOverlay::renderControllerPointers() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); @@ -365,13 +369,12 @@ void ApplicationOverlay::renderControllerPointer() { //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - pointerWidth /= 2; - pointerHeight /= 2; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; _numMagnifiers++; - + //If oculus is enabled, we draw the crosshairs later + continue; } mouseX -= pointerWidth / 2.0f; @@ -390,13 +393,74 @@ void ApplicationOverlay::renderControllerPointer() { } } +void ApplicationOverlay::renderControllerPointersOculus() { + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + + const float reticleSize = 50.0f; + + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + glDisable(GL_DEPTH_TEST); + + for (int i = 0; i < _numMagnifiers; i++) { + + float mouseX = (float)_mouseX[i]; + float mouseY = (float)_mouseY[i]; + mouseX -= reticleSize / 2; + mouseY += reticleSize / 2; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = mouseX / widgetWidth; + float newURight = (mouseX + reticleSize) / widgetWidth; + float newVBottom = 1.0 - mouseY / widgetHeight; + float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); + glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); + glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); + + glEnd(); + } + } + glEnable(GL_DEPTH_TEST); +} + //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); - const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); float leftX, rightX, leftZ, rightZ, topZ, bottomZ; diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index dc1d3d641f..71f78d606d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -44,7 +44,8 @@ private: typedef QPair VerticesIndices; void renderPointers(); - void renderControllerPointer(); + void renderControllerPointers(); + void renderControllerPointersOculus(); void renderMagnifier(int mouseX, int mouseY); void renderAudioMeter(); void renderStatsAndLogs(); From 53d4cc795a62b767f27c975267ba37de596afe7f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:09:45 -0700 Subject: [PATCH 74/99] Removed unused UI types that clutter code --- interface/src/ui/ApplicationOverlay.cpp | 272 ++++++++---------------- interface/src/ui/ApplicationOverlay.h | 7 +- 2 files changed, 85 insertions(+), 194 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index fe6baadf66..2ef5665bee 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -34,7 +34,6 @@ ApplicationOverlay::ApplicationOverlay() : _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), - _uiType(HEMISPHERE), _crosshairTexture(0) { } @@ -157,25 +156,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); - const float halfHorizontalAngle = horizontalAngle / 2; - - const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); glActiveTexture(GL_TEXTURE0); @@ -211,8 +191,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.01f); - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - //Draw the magnifiers for (int i = 0; i < _numMagnifiers; i++) { renderMagnifier(_mouseX[i], _mouseY[i]); @@ -221,42 +199,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); - //TODO: Remove immediate mode in favor of VBO - if (_uiType == HEMISPHERE) { - renderTexturedHemisphere(); - } else{ - glBegin(GL_QUADS); - // Place the vertices in a semicircle curve around the camera - for (int i = 0; i < numHorizontalVertices - 1; i++) { - for (int j = 0; j < numVerticalVertices - 1; j++) { - - // Calculate the X and Z coordinates from the angles and radius from camera - leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; - rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; - rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - if (_uiType == 2) { - topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; - bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; - } else { - topZ = -99999; - bottomZ = -99999; - } - - glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); - glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); - glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); - glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); - glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); - glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); - glTexCoord2f(quadTexWidth * i, j * quadTexHeight); - glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); - } - } - - glEnd(); - } - + renderTexturedHemisphere(); + renderControllerPointersOculus(); glPopMatrix(); @@ -411,106 +355,12 @@ void ApplicationOverlay::renderControllerPointersOculus() { float mouseY = (float)_mouseY[i]; mouseX -= reticleSize / 2; mouseY += reticleSize / 2; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - - //Get new UV coordinates from our magnification window - float newULeft = mouseX / widgetWidth; - float newURight = (mouseX + reticleSize) / widgetWidth; - float newVBottom = 1.0 - mouseY / widgetHeight; - float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; - - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * _textureFov); - float rX = sin((newURight - 0.5f) * _textureFov); - float bY = sin((newVBottom - 0.5f) * _textureFov); - float tY = sin((newVTop - 0.5f) * _textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); - - glBegin(GL_QUADS); - - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); - - glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); - glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); - glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); - - glEnd(); - } - } - glEnable(GL_DEPTH_TEST); -} - -//Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) -{ - Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); - - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; - - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float halfHorizontalAngle = horizontalAngle / 2; - - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; - - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - + //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + float newULeft = mouseX / widgetWidth; + float newURight = (mouseX + reticleSize) / widgetWidth; + float newVBottom = 1.0 - mouseY / widgetHeight; + float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; // Project our position onto the hemisphere using the UV coordinates float lX = sin((newULeft - 0.5f) * _textureFov); @@ -534,40 +384,86 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) glBegin(GL_QUADS); - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; - } - - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; - - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); + glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); + glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); glEnd(); + } + glEnable(GL_DEPTH_TEST); +} + +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; + + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; + + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; + + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; + + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get position on hemisphere using angle + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + } void ApplicationOverlay::renderAudioMeter() { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 71f78d606d..53a0125dae 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -19,8 +19,6 @@ class QOpenGLFramebufferObject; class ApplicationOverlay { public: - enum UIType { HEMISPHERE, SEMICIRCLE, CURVED_SEMICIRCLE }; - ApplicationOverlay(); ~ApplicationOverlay(); @@ -31,9 +29,7 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); - - void setUIType(UIType uiType) { _uiType = uiType; } - + private: // Interleaved vertex data struct TextureVertex { @@ -56,7 +52,6 @@ private: float _oculusAngle; float _distance; float _textureFov; - UIType _uiType; int _mouseX[2]; int _mouseY[2]; int _numMagnifiers; From bef625d237a14dd12bf36c79d61518ab919bc515 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:13:43 -0700 Subject: [PATCH 75/99] Removed pointless Code --- interface/src/ui/ApplicationOverlay.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2ef5665bee..13630290fd 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -234,33 +234,10 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 20; - const float pointerHeight = 20; - + //If we are in oculus, render reticle later _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); _mouseY[0] = application->getMouseY(); - - //If we are in oculus, render reticle later - - - if (!OculusManager::isConnected()) { - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - glBegin(GL_QUADS); - - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); - - //Horizontal crosshair - glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); - - glEnd(); - } } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointers(); From 3fbde70f96694979d62a5ae22ae0f8bf17f147f1 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 15 Jun 2014 21:05:23 +0200 Subject: [PATCH 76/99] Added functions to test usleep, msleep and sleep for accuracy. --- libraries/shared/src/SharedUtil.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index f29e8e3345..06a5ab8137 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "OctalCode.h" #include "SharedUtil.h" @@ -415,13 +416,16 @@ void printVoxelCode(unsigned char* voxelCode) { #ifdef _WIN32 void usleep(int waitTime) { - __int64 time1 = 0, time2 = 0, sysFreq = 0; - - QueryPerformanceCounter((LARGE_INTEGER *)&time1); - QueryPerformanceFrequency((LARGE_INTEGER *)&sysFreq); - do { - QueryPerformanceCounter((LARGE_INTEGER *)&time2); - } while( (time2 - time1) < waitTime); + quint64 compTime = waitTime + usecTimestampNow(); + quint64 compTimeSleep = compTime - 2000; + while (true) { + if (usecTimestampNow() < compTimeSleep) { + QThread::msleep(1); + } + if (usecTimestampNow() >= compTime) { + break; + } + } } #endif From 05900420a56ee37087f88a72fa84ee955850a5ae Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 15 Jun 2014 21:07:03 +0200 Subject: [PATCH 77/99] Replaced windows usleep version with a non busy waiting high accuracy version. This fixes the high CPU load for the windows servers too! --- interface/src/Util.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 07ca65b286..5ce1435bd6 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -21,6 +21,8 @@ #include +#include + #include "InterfaceConfig.h" #include "ui/TextRenderer.h" #include "VoxelConstants.h" @@ -409,8 +411,44 @@ void runTimingTests() { float NSEC_TO_USEC = 1.0f / 1000.0f; elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; - qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs / (float) numTests); + qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs); + // Test sleep functions for accuracy + startTime.start(); + QThread::msleep(1); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("QThread::msleep(1) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + QThread::sleep(1); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("QThread::sleep(1) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(1); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(1) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(10); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(10) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(100); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(100) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(1000); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(1000) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(15000); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(15000) ms: %f", elapsedUsecs / 1000.0f); + // Random number generation startTime.start(); for (int i = 0; i < numTests; i++) { From 8c700d43f34ef26bc389ad2f2374b9e57b2c1289 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 15 Jun 2014 21:08:14 +0200 Subject: [PATCH 78/99] Modified CALLBACK_ACCELERATOR_RATIO for windows because some systems need larger input buffer to work fine. --- interface/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 2cbe3a062f..271bcd5279 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1419,7 +1419,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) // proportional to the accelerator ratio. #ifdef Q_OS_WIN -const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.4f; +const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.1f; #endif #ifdef Q_OS_MAC From 94e58e9559bff84fd60233acdf3aaa3f9b4d314f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 10:04:56 -0700 Subject: [PATCH 79/99] Fixed coding standard and made getCursorPixelRangeMultiplier more clear --- interface/src/devices/SixenseManager.cpp | 10 ++++++++-- interface/src/ui/ApplicationOverlay.cpp | 8 +++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index ed55dc4c66..43dadaef1c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,9 +185,14 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } +const float MAXIMUM_PIXEL_RANGE_MULT = 2.0f; +const float MINIMUM_PIXEL_RANGE_MULT = 0.4f; +const float RANGE_MULT = (MAXIMUM_PIXEL_RANGE_MULT - MINIMUM_PIXEL_RANGE_MULT) * 0.01; + +//Returns a multiplier to be applied to the cursor range for the controllers float SixenseManager::getCursorPixelRangeMultiplier() const { - //scales (0,100) to (0.4,2.0) - return ((Menu::getInstance()->getSixenseReticleMoveSpeed()) * 0.008f + 0.2f) * 2.0f; + //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) + return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MINIMUM_PIXEL_RANGE_MULT; } #ifdef HAVE_SIXENSE @@ -356,6 +361,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float xAngle = (atan2(direction.z, direction.x) + M_PI_2); float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); pos.setX(widget->width() / 2.0f + cursorRange * xAngle); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 13630290fd..aedc81facc 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -46,6 +46,8 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; + // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); @@ -222,7 +224,6 @@ void ApplicationOverlay::renderPointers() { int mouseX = application->getMouseX(); int mouseY = application->getMouseY(); - //lazily load crosshair texture if (_crosshairTexture == 0) { _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); @@ -274,6 +275,7 @@ void ApplicationOverlay::renderControllerPointers() { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) ; float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; @@ -303,7 +305,7 @@ void ApplicationOverlay::renderControllerPointers() { glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f/255.0f, 244.0f/255.0f); + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); @@ -361,7 +363,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); From 031fe323ea5e98b5d37167da3c983ce7960d45b8 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 10:12:09 -0700 Subject: [PATCH 80/99] Got rid of some verbosity --- interface/src/devices/SixenseManager.cpp | 13 +++++++------ interface/src/devices/SixenseManager.h | 2 +- interface/src/ui/ApplicationOverlay.cpp | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 43dadaef1c..2c62a58800 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,14 +185,15 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } -const float MAXIMUM_PIXEL_RANGE_MULT = 2.0f; -const float MINIMUM_PIXEL_RANGE_MULT = 0.4f; -const float RANGE_MULT = (MAXIMUM_PIXEL_RANGE_MULT - MINIMUM_PIXEL_RANGE_MULT) * 0.01; +//Constants for getCursorPixelRangeMultiplier() +const float MIN_PIXEL_RANGE_MULT = 0.4f; +const float MAX_PIXEL_RANGE_MULT = 2.0f; +const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01; //Returns a multiplier to be applied to the cursor range for the controllers -float SixenseManager::getCursorPixelRangeMultiplier() const { +float SixenseManager::getCursorPixelRangeMult() const { //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) - return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MINIMUM_PIXEL_RANGE_MULT; + return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MIN_PIXEL_RANGE_MULT; } #ifdef HAVE_SIXENSE @@ -362,7 +363,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); + float cursorRange = widget->width() * getCursorPixelRangeMult(); pos.setX(widget->width() / 2.0f + cursorRange * xAngle); pos.setY(widget->height() / 2.0f + cursorRange * yAngle); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 41e061a25b..8803c2c006 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -42,7 +42,7 @@ public: ~SixenseManager(); void update(float deltaTime); - float getCursorPixelRangeMultiplier() const; + float getCursorPixelRangeMult() const; public slots: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index aedc81facc..471ff20d66 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -276,7 +276,7 @@ void ApplicationOverlay::renderControllerPointers() { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult(); int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; From af3b2a9d0f6c4f8e332e5a766915d53d309afad0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jun 2014 10:43:13 -0700 Subject: [PATCH 81/99] Update LocationManager::goTo to use new addresses API The new API address is now used for goTo when # or @ isn't present Fixed the multiple locations popup to show when multiple locations are found --- interface/src/Menu.cpp | 8 ++- interface/src/avatar/MyAvatar.cpp | 64 +++++++++++----------- interface/src/avatar/MyAvatar.h | 1 + interface/src/location/LocationManager.cpp | 57 ++++++------------- interface/src/location/LocationManager.h | 7 +-- 5 files changed, 58 insertions(+), 79 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0c6a3efa24..46b2bfc806 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1002,7 +1002,9 @@ bool Menu::goToDestination(QString destination) { } void Menu::goTo(QString destination) { - LocationManager::getInstance().goTo(destination); + LocationManager* manager = &LocationManager::getInstance(); + manager->goTo(destination); + connect(manager, &LocationManager::multipleDestinationsFound, getInstance(), &Menu::multipleDestinationsDecision); } void Menu::goTo() { @@ -1091,9 +1093,9 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson int userResponse = msgBox.exec(); if (userResponse == QMessageBox::Ok) { - Application::getInstance()->getAvatar()->goToLocationFromResponse(userData); + Application::getInstance()->getAvatar()->goToLocationFromAddress(userData["address"].toObject()); } else if (userResponse == QMessageBox::Open) { - Application::getInstance()->getAvatar()->goToLocationFromResponse(userData); + Application::getInstance()->getAvatar()->goToLocationFromAddress(placeData["address"].toObject()); } LocationManager* manager = reinterpret_cast(sender()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a14152aa04..54ed641d72 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1626,44 +1626,46 @@ void MyAvatar::resetSize() { } void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { - if (jsonObject["status"].toString() == "success") { - - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - sendKillAvatar(); - QJsonObject locationObject = jsonObject["data"].toObject()["address"].toObject(); - QString positionString = locationObject["position"].toString(); - QString orientationString = locationObject["orientation"].toString(); - QString domainHostnameString = locationObject["domain"].toString(); - - qDebug() << "Changing domain to" << domainHostnameString << - ", position to" << positionString << - ", and orientation to" << orientationString; - - QStringList coordinateItems = positionString.split(','); - QStringList orientationItems = orientationString.split(','); - - NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString); - - // orient the user to face the target - glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), - orientationItems[1].toFloat(), - orientationItems[2].toFloat()))) - * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - setOrientation(newOrientation); - - // move the user a couple units away - const float DISTANCE_TO_USER = 2.0f; - glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), - coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; - setPosition(newPosition); - emit transformChanged(); + goToLocationFromAddress(locationObject); } else { QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found."); } } +void MyAvatar::goToLocationFromAddress(const QJsonObject& locationObject) { + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + sendKillAvatar(); + + QString positionString = locationObject["position"].toString(); + QString orientationString = locationObject["orientation"].toString(); + QString domainHostnameString = locationObject["domain"].toString(); + + qDebug() << "Changing domain to" << domainHostnameString << + ", position to" << positionString << + ", and orientation to" << orientationString; + + QStringList coordinateItems = positionString.split(','); + QStringList orientationItems = orientationString.split(','); + + NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString); + + // orient the user to face the target + glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), + orientationItems[1].toFloat(), + orientationItems[2].toFloat()))) + * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + setOrientation(newOrientation); + + // move the user a couple units away + const float DISTANCE_TO_USER = 2.0f; + glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), + coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; + setPosition(newPosition); + emit transformChanged(); +} + void MyAvatar::updateMotionBehaviorsFromMenu() { Menu* menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d99102c356..2fbc488feb 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -129,6 +129,7 @@ public slots: void resetSize(); void goToLocationFromResponse(const QJsonObject& jsonObject); + void goToLocationFromAddress(const QJsonObject& jsonObject); // Set/Get update the thrust that will move the avatar around void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index f80c331df4..f1fd5e71f1 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -16,10 +16,11 @@ const QString GET_USER_ADDRESS = "/api/v1/users/%1/address"; const QString GET_PLACE_ADDRESS = "/api/v1/places/%1/address"; +const QString GET_ADDRESSES = "/api/v1/addresses/%1"; const QString POST_PLACE_CREATE = "/api/v1/places/"; -LocationManager::LocationManager() : _userData(), _placeData() { +LocationManager::LocationManager() { }; @@ -74,59 +75,37 @@ void LocationManager::goTo(QString destination) { // go to coordinate destination or to Username if (!goToDestination(destination)) { - // reset data on local variables - _userData = QJsonObject(); - _placeData = QJsonObject(); - destination = QString(QUrl::toPercentEncoding(destination)); JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "goToUserFromResponse"; - AccountManager::getInstance().authenticatedRequest(GET_USER_ADDRESS.arg(destination), - QNetworkAccessManager::GetOperation, - callbackParams); - - callbackParams.jsonCallbackMethod = "goToLocationFromResponse"; - AccountManager::getInstance().authenticatedRequest(GET_PLACE_ADDRESS.arg(destination), + callbackParams.jsonCallbackMethod = "goToAddressFromResponse"; + AccountManager::getInstance().authenticatedRequest(GET_ADDRESSES.arg(destination), QNetworkAccessManager::GetOperation, callbackParams); } } -void LocationManager::goToUserFromResponse(const QJsonObject& jsonObject) { - _userData = jsonObject; - checkForMultipleDestinations(); -} +void LocationManager::goToAddressFromResponse(const QJsonObject& responseData) { + QJsonValue status = responseData["status"]; + qDebug() << responseData; + if (!status.isUndefined() && status.toString() == "success") { + const QJsonObject& data = responseData["data"].toObject(); + const QJsonValue& userObject = data["user"]; + const QJsonValue& placeObject = data["place"]; -void LocationManager::goToLocationFromResponse(const QJsonObject& jsonObject) { - _placeData = jsonObject; - checkForMultipleDestinations(); -} - -void LocationManager::checkForMultipleDestinations() { - if (!_userData.isEmpty() && !_placeData.isEmpty()) { - if (_userData.contains("status") && _userData["status"].toString() == "success" && - _placeData.contains("status") && _placeData["status"].toString() == "success") { - emit multipleDestinationsFound(_userData, _placeData); - return; + if (!placeObject.isUndefined() && !userObject.isUndefined()) { + emit multipleDestinationsFound(userObject.toObject(), placeObject.toObject()); + } else if (placeObject.isUndefined()) { + Application::getInstance()->getAvatar()->goToLocationFromAddress(userObject.toObject()["address"].toObject()); + } else { + Application::getInstance()->getAvatar()->goToLocationFromAddress(placeObject.toObject()["address"].toObject()); } - - if (_userData.contains("status") && _userData["status"].toString() == "success") { - Application::getInstance()->getAvatar()->goToLocationFromResponse(_userData); - return; - } - - if (_placeData.contains("status") && _placeData["status"].toString() == "success") { - Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData); - return; - } - + } else { QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found."); } } void LocationManager::goToUser(QString userName) { - JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar(); callbackParams.jsonCallbackMethod = "goToLocationFromResponse"; diff --git a/interface/src/location/LocationManager.h b/interface/src/location/LocationManager.h index ac66b3d08b..b781f3f54e 100644 --- a/interface/src/location/LocationManager.h +++ b/interface/src/location/LocationManager.h @@ -39,11 +39,7 @@ public: bool goToDestination(QString destination); private: - QJsonObject _userData; - QJsonObject _placeData; - void replaceLastOccurrence(const QChar search, const QChar replace, QString& string); - void checkForMultipleDestinations(); signals: void creationCompleted(LocationManager::NamedLocationCreateResponse response); @@ -52,8 +48,7 @@ signals: private slots: void namedLocationDataReceived(const QJsonObject& data); void errorDataReceived(QNetworkReply::NetworkError error, const QString& message); - void goToLocationFromResponse(const QJsonObject& jsonObject); - void goToUserFromResponse(const QJsonObject& jsonObject); + void goToAddressFromResponse(const QJsonObject& jsonObject); }; From d5aa12db7d9bb99d634f9e08796a2141baca57b2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 16 Jun 2014 10:55:05 -0700 Subject: [PATCH 82/99] Added models support to inspect.js --- examples/inspect.js | 49 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/examples/inspect.js b/examples/inspect.js index 28db1e7735..af63957ec3 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -177,30 +177,45 @@ function keyReleaseEvent(event) { function mousePressEvent(event) { if (alt && !isActive) { - isActive = true; mouseLastX = event.x; mouseLastY = event.y; // Compute trajectories related values var pickRay = Camera.computePickRay(mouseLastX, mouseLastY); - var intersection = Voxels.findRayIntersection(pickRay); + var voxelIntersection = Voxels.findRayIntersection(pickRay); + var modelIntersection = Models.findRayIntersection(pickRay); position = Camera.getPosition(); - avatarTarget = MyAvatar.getTargetAvatarPosition(); - voxelTarget = intersection.intersection; - if (Vec3.length(Vec3.subtract(avatarTarget, position)) < Vec3.length(Vec3.subtract(voxelTarget, position))) { - if (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0) { - center = avatarTarget; - } else { - center = voxelTarget; - } - } else { - if (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0) { - center = voxelTarget; - } else { - center = avatarTarget; - } + var avatarTarget = MyAvatar.getTargetAvatarPosition(); + var voxelTarget = voxelIntersection.intersection; + + + var distance = -1; + var string; + + if (modelIntersection.intersects && modelIntersection.accurate) { + distance = modelIntersection.distance; + center = modelIntersection.modelProperties.position; + string = "Inspecting model"; + } + + if ((distance == -1 || Vec3.length(Vec3.subtract(avatarTarget, position)) < distance) && + (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0)) { + distance = Vec3.length(Vec3.subtract(avatarTarget, position)); + center = avatarTarget; + string = "Inspecting avatar"; + } + + if ((distance == -1 || Vec3.length(Vec3.subtract(voxelTarget, position)) < distance) && + (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0)) { + distance = Vec3.length(Vec3.subtract(voxelTarget, position)); + center = voxelTarget; + string = "Inspecting voxel"; + } + + if (distance == -1) { + return; } vector = Vec3.subtract(position, center); @@ -209,6 +224,8 @@ function mousePressEvent(event) { altitude = Math.asin(vector.y / Vec3.length(vector)); Camera.keepLookingAt(center); + print(string); + isActive = true; } } From b3b3a72946202da452d1846879fa5f271047c899 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 16 Jun 2014 10:55:34 -0700 Subject: [PATCH 83/99] Disable editModels.js mouse events when ALT is pressed. (no conflict with inspect.js) --- examples/editModels.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/examples/editModels.js b/examples/editModels.js index 93a34b9a3a..53633d3c0c 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -691,6 +691,10 @@ function rayPlaneIntersection(pickRay, point, normal) { } function mousePressEvent(event) { + if (altIsPressed) { + return; + } + mouseLastPosition = { x: event.x, y: event.y }; modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); @@ -790,6 +794,10 @@ var oldModifier = 0; var modifier = 0; var wasShifted = false; function mouseMoveEvent(event) { + if (altIsPressed) { + return; + } + var pickRay = Camera.computePickRay(event.x, event.y); if (!modelSelected) { @@ -894,6 +902,10 @@ function mouseMoveEvent(event) { } function mouseReleaseEvent(event) { + if (altIsPressed) { + return; + } + modelSelected = false; glowedModelID.id = -1; @@ -962,4 +974,16 @@ Menu.menuItemEvent.connect(function(menuItem){ } }); +// handling of inspect.js concurrence +altIsPressed = false; +Controller.keyPressEvent.connect(function(event) { + if (event.text == "ALT") { + altIsPressed = true; + } +}); +Controller.keyReleaseEvent.connect(function(event) { + if (event.text == "ALT") { + altIsPressed = false; + } +}); From 978b7521dba30fb90e5eb39a2a5075dcd9fcd1ff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jun 2014 11:30:47 -0700 Subject: [PATCH 84/99] Move connect call for multiple destinations to constructor --- interface/src/Menu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 46b2bfc806..8f023e607f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -165,6 +165,8 @@ Menu::Menu() : Qt::Key_At, this, SLOT(goTo())); + connect(&LocationManager::getInstance(), &LocationManager::multipleDestinationsFound, + this, &Menu::multipleDestinationsDecision); addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead())); @@ -1002,9 +1004,7 @@ bool Menu::goToDestination(QString destination) { } void Menu::goTo(QString destination) { - LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(destination); - connect(manager, &LocationManager::multipleDestinationsFound, getInstance(), &Menu::multipleDestinationsDecision); + LocationManager::getInstance().goTo(destination); } void Menu::goTo() { From 9faf9d7749d4f50d4a03d6b44cbb43314ad86b36 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jun 2014 11:43:04 -0700 Subject: [PATCH 85/99] Remove extra connect and disconnect for multipleDestinationsDecision --- interface/src/Menu.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f2f68a3594..f4fb8a363f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1074,9 +1074,7 @@ bool Menu::goToURL(QString location) { } void Menu::goToUser(const QString& user) { - LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(user); - connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); + LocationManager::getInstance().goTo(user); } /// Open a url, shortcutting any "hifi" scheme URLs to the local application. @@ -1104,7 +1102,6 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson } LocationManager* manager = reinterpret_cast(sender()); - disconnect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); } void Menu::muteEnvironment() { From d08b63b2476244721ae1d601ad47d681bae28ea3 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Mon, 16 Jun 2014 23:30:28 +0200 Subject: [PATCH 86/99] changed 2000 into a constant defined inline --- libraries/shared/src/SharedUtil.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 06a5ab8137..e4d2e1c835 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -416,8 +416,9 @@ void printVoxelCode(unsigned char* voxelCode) { #ifdef _WIN32 void usleep(int waitTime) { + const quint64 BUSY_LOOP_USECS = 2000; quint64 compTime = waitTime + usecTimestampNow(); - quint64 compTimeSleep = compTime - 2000; + quint64 compTimeSleep = compTime - BUSY_LOOP_USECS; while (true) { if (usecTimestampNow() < compTimeSleep) { QThread::msleep(1); From 5843425db90a5269ba47d46db00854de2e54f630 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 17 Jun 2014 09:18:58 -0700 Subject: [PATCH 87/99] Fix scripts being loaded on start even if they don't exist Application::loadScript should not store a reference to a script until it has been successfully loaded. Previously if a script didn't exist it would be "loaded" and show up in the running scripts window, but wouldn't have been successfully loaded, and wouldn't be running anything at all. --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6b44503af4..bc6841e626 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3523,12 +3523,13 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript } else { // start the script on a new thread... scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptURLString, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; return NULL; } + + _scriptEnginesHash.insert(scriptURLString, scriptEngine); _runningScriptsWidget->setRunningScripts(getRunningScripts()); } From f367e1840d20e9c758c5b41a9e01d65da6b22cbd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 17 Jun 2014 10:37:35 -0700 Subject: [PATCH 88/99] Added a detached mode by default in inpect.js --- examples/inspect.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/examples/inspect.js b/examples/inspect.js index af63957ec3..b292d5f609 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -34,6 +34,7 @@ var noMode = 0; var orbitMode = 1; var radialMode = 2; var panningMode = 3; +var detachedMode = 4; var mode = noMode; @@ -48,6 +49,9 @@ var radius = 0.0; var azimuth = 0.0; var altitude = 0.0; +var avatarPosition; +var avatarOrientation; + function handleRadialMode(dx, dy) { azimuth += dx / AZIMUTH_RATE; @@ -108,7 +112,7 @@ function restoreCameraState() { } function handleModes() { - var newMode = noMode; + var newMode = (mode == noMode) ? noMode : detachedMode; if (alt) { if (control) { if (shift) { @@ -121,6 +125,22 @@ function handleModes() { } } + // if entering detachMode + if (newMode == detachedMode && mode != detachedMode) { + avatarPosition = MyAvatar.position; + avatarOrientation = MyAvatar.orientation; + } + // if leaving detachMode + if (mode == detachedMode && newMode == detachedMode && + (avatarPosition.x != MyAvatar.position.x || + avatarPosition.y != MyAvatar.position.y || + avatarPosition.z != MyAvatar.position.z || + avatarOrientation.x != MyAvatar.orientation.x || + avatarOrientation.y != MyAvatar.orientation.y || + avatarOrientation.z != MyAvatar.orientation.z || + avatarOrientation.w != MyAvatar.orientation.w)) { + newMode = noMode; + } // if leaving noMode if (mode == noMode && newMode != noMode) { saveCameraState(); @@ -252,6 +272,10 @@ function mouseMoveEvent(event) { } } +function update() { + handleModes(); +} + function scriptEnding() { if (mode != noMode) { restoreCameraState(); @@ -265,4 +289,5 @@ Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); +Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From e75340f8ceb2b966e5ae7c1b8acf346ea6490d3a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 10:48:29 -0700 Subject: [PATCH 89/99] Add PlaneShape::getNormal() method. --- libraries/shared/src/PlaneShape.cpp | 4 ++++ libraries/shared/src/PlaneShape.h | 1 + 2 files changed, 5 insertions(+) diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index a8b4468c93..0617feb863 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -30,6 +30,10 @@ PlaneShape::PlaneShape(const glm::vec4& coefficients) : } } +glm::vec3 PlaneShape::getNormal() const { + return _rotation * UNROTATED_NORMAL; +} + glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position)); diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h index 524d53ec73..24a3f1a2bc 100644 --- a/libraries/shared/src/PlaneShape.h +++ b/libraries/shared/src/PlaneShape.h @@ -18,6 +18,7 @@ class PlaneShape : public Shape { public: PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); + glm::vec3 getNormal() const; glm::vec4 getCoefficients() const; }; From ab3d582d79ca08001ef87cdf486ef3d28e63b5e5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 10:49:14 -0700 Subject: [PATCH 90/99] Add ray intersection tests against most shapes. --- libraries/shared/src/ShapeCollider.cpp | 81 ++++++++++++++++++++++++ libraries/shared/src/ShapeCollider.h | 88 +++++++++++++++----------- 2 files changed, 132 insertions(+), 37 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 7c29fbae00..24d7fdb01a 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -765,5 +765,86 @@ bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, fl return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); } +bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& minDistance) { + float hitDistance = FLT_MAX; + int numShapes = shapes.size(); + for (int i = 0; i < numShapes; ++i) { + Shape* shape = shapes.at(i); + if (shape) { + float distance; + if (findRayIntersectionWithShape(shape, rayStart, rayDirection, distance)) { + if (distance < hitDistance) { + hitDistance = distance; + } + } + } + } + if (hitDistance < FLT_MAX) { + minDistance = hitDistance; + } + return false; +} + +bool findRayIntersectionWithShape(const Shape* shape, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) { + // NOTE: rayDirection is assumed to be normalized + int typeA = shape->getType(); + if (typeA == Shape::SPHERE_SHAPE) { + const SphereShape* sphere = static_cast(shape); + glm::vec3 sphereCenter = sphere->getPosition(); + float r2 = sphere->getRadius() * sphere->getRadius(); // r2 = radius^2 + + // compute closest approach (CA) + float a = glm::dot(sphere->getPosition() - rayStart, rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(sphereCenter, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA + if (b2 > r2) { + // ray does not hit sphere + return false; + } + float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection + float d2 = glm::distance2(rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start + if (a < 0.0f) { + // ray points away from sphere-center + if (d2 > r2) { + // ray starts outside sphere + return false; + } + // ray starts inside sphere + distance = c + a; + } else if (d2 > r2) { + // ray starts outside sphere + distance = a - c; + } else { + // ray starts inside sphere + distance = a + c; + } + return true; + } else if (typeA == Shape::CAPSULE_SHAPE) { + const CapsuleShape* capsule = static_cast(shape); + float radius = capsule->getRadius(); + glm::vec3 capsuleStart, capsuleEnd; + capsule->getStartPoint(capsuleStart); + capsule->getEndPoint(capsuleEnd); + // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. + // TODO: implement the raycast to return inside surface intersection for the internal rayStart. + return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, radius, distance); + } else if (typeA == Shape::PLANE_SHAPE) { + const PlaneShape* plane = static_cast(shape); + glm::vec3 n = plane->getNormal(); + glm::vec3 P = plane->getPosition(); + float denominator = glm::dot(n, rayDirection); + if (fabsf(denominator) < EPSILON) { + // line is parallel to plane + return glm::dot(P - rayStart, n) < EPSILON; + } else { + float d = glm::dot(P - rayStart, n) / denominator; + if (d > 0.0f) { + // ray points toward plane + distance = d; + return true; + } + } + } + return false; +} } // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 9e83e31571..308a8cf10b 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -21,8 +21,8 @@ namespace ShapeCollider { - /// \param shapeA pointer to first shape - /// \param shapeB pointer to second shape + /// \param shapeA pointer to first shape (cannot be NULL) + /// \param shapeB pointer to second shape (cannot be NULL) /// \param collisions[out] collision details /// \return true if shapes collide bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); @@ -33,123 +33,137 @@ namespace ShapeCollider { /// \return true if any shapes collide bool collideShapesCoarse(const QVector& shapesA, const QVector& shapesB, CollisionInfo& collision); - /// \param shapeA a pointer to a shape + /// \param shapeA a pointer to a shape (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param collisions[out] average collision details /// \return true if shapeA collides with axis aligned cube bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param sphereB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param planeB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions); - /// \param capsuleA pointer to first shape - /// \param sphereB pointer to second shape + /// \param capsuleA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions); - /// \param capsuleA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param capsuleA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param capsuleA pointer to first shape - /// \param planeB pointer to second shape + /// \param capsuleA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param sphereB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param planeB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param listB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions); - /// \param capuleA pointer to first shape - /// \param listB pointer to second shape + /// \param capuleA pointer to first shape (cannot be NULL) + /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param listB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param sphereB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param planeB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions); - /// \param sphereA pointer to sphere + /// \param sphereA pointer to sphere (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param[out] collisions where to append collision details /// \return true if sphereA collides with axis aligned cube bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - /// \param capsuleA pointer to capsule + /// \param capsuleA pointer to capsule (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param[out] collisions where to append collision details /// \return true if capsuleA collides with axis aligned cube bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); + /// \param shapes list of pointers to shapes (shape pointers may be NULL) + /// \param startPoint beginning of ray + /// \param direction direction of ray + /// \param minDistance[out] shortest distance to intersection of ray with a shapes + /// \return true if ray hits any shape in shapes + bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& startPoint, const glm::vec3& direction, float& minDistance); + + /// \param shapeA pointer to shape (cannot be NULL) + /// \param startPoint beginning of ray + /// \param direction direction of ray + /// \param distance[out] distance to intersection of shape and ray + /// \return true if ray hits shapeA + bool findRayIntersectionWithShape(const Shape* shapeA, const glm::vec3& startPoint, const glm::vec3& direction, float& distance); + } // namespace ShapeCollider #endif // hifi_ShapeCollider_h From 1e200c3b9ce4d366611d79eed656b4309406c833 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 17 Jun 2014 19:49:35 +0200 Subject: [PATCH 91/99] The balance display didn't get over ~21.1 because the parsed json object int32 is maximal 2147483647, fixed this by parsing it from json to double. --- libraries/networking/src/DataServerAccountInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index c60a17e0d8..507c085d26 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -85,7 +85,7 @@ void DataServerAccountInfo::setBalance(qint64 balance) { void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { if (jsonObject["status"].toString() == "success") { - qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toInt(); + qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble(); setBalance(balanceInSatoshis); } } From fa6aed3e01a9d004cca5de4fa12c7538f37c35e9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 10:49:53 -0700 Subject: [PATCH 92/99] Add unit tests for ray intersections with shapes. --- tests/physics/src/ShapeColliderTests.cpp | 408 +++++++++++++++++++++++ tests/physics/src/ShapeColliderTests.h | 8 + 2 files changed, 416 insertions(+) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 7b3d956065..3387ba6aba 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -11,6 +11,7 @@ //#include #include +#include #include #include @@ -897,6 +898,405 @@ void ShapeColliderTests::sphereMissesAACube() { } } +void ShapeColliderTests::rayHitsSphere() { + float startDistance = 3.0f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + + float radius = 1.0f; + glm::vec3 center(0.0f); + + SphereShape sphere(radius, center); + + // very simple ray along xAxis + { + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; + } + + float expectedDistance = startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + } + } + + // ray along a diagonal axis + { + rayStart = glm::vec3(startDistance, startDistance, 0.0f); + rayDirection = - glm::normalize(rayStart); + + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; + } + + float expectedDistance = SQUARE_ROOT_OF_2 * startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + } + } + + // rotated and displaced ray and sphere + { + startDistance = 7.41f; + radius = 3.917f; + + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); + + glm::vec3 unrotatedRayDirection(-1.0f, 0.0f, 0.0f); + glm::vec3 untransformedRayStart(startDistance, 0.0f, 0.0f); + + rayStart = rotation * (untransformedRayStart + translation); + rayDirection = rotation * unrotatedRayDirection; + + sphere.setRadius(radius); + sphere.setPosition(rotation * translation); + + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; + } + + float expectedDistance = startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + } + } +} + +void ShapeColliderTests::rayBarelyHitsSphere() { + float radius = 1.0f; + glm::vec3 center(0.0f); + float delta = 2.0f * EPSILON; + + float startDistance = 3.0f; + glm::vec3 rayStart(-startDistance, radius - delta, 0.0f); + glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + + SphereShape sphere(radius, center); + + // very simple ray along xAxis + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + } + + // translate and rotate the whole system... + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); + + rayStart = rotation * (rayStart + translation); + rayDirection = rotation * rayDirection; + sphere.setPosition(rotation * translation); + + // ...and test again + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + } +} + + +void ShapeColliderTests::rayBarelyMissesSphere() { + // same as the barely-hits case, but this time we move the ray away from sphere + float radius = 1.0f; + glm::vec3 center(0.0f); + float delta = 2.0f * EPSILON; + + float startDistance = 3.0f; + glm::vec3 rayStart(-startDistance, radius + delta, 0.0f); + glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + + SphereShape sphere(radius, center); + + // very simple ray along xAxis + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // translate and rotate the whole system... + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); + + rayStart = rotation * (rayStart + translation); + rayDirection = rotation * rayDirection; + sphere.setPosition(rotation * translation); + + // ...and test again + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } +} + +void ShapeColliderTests::rayHitsCapsule() { + float startDistance = 3.0f; + float radius = 1.0f; + float halfHeight = 2.0f; + glm::vec3 center(0.0f); + CapsuleShape capsule(radius, halfHeight); + + { // simple test along xAxis + // toward capsule center + glm::vec3 rayStart(startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + float expectedDistance = startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward top of cylindrical wall + rayStart.y = halfHeight; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward top cap + float delta = 2.0f * EPSILON; + rayStart.y = halfHeight + delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + const float EDGE_CASE_SLOP_FACTOR = 20.0f; + + // toward tip of top cap + rayStart.y = halfHeight + radius - delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + relativeError = fabsf(distance - expectedDistance) / startDistance; + // for edge cases we allow a LOT of error + if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward tip of bottom cap + rayStart.y = - halfHeight - radius + delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + relativeError = fabsf(distance - expectedDistance) / startDistance; + // for edge cases we allow a LOT of error + if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward edge of capsule cylindrical face + rayStart.y = 0.0f; + rayStart.z = radius - delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + relativeError = fabsf(distance - expectedDistance) / startDistance; + // for edge cases we allow a LOT of error + if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + } + // TODO: test at steep angles near cylinder/cap junction +} + +void ShapeColliderTests::rayMissesCapsule() { + // same as edge case hit tests, but shifted in the opposite direction + float startDistance = 3.0f; + float radius = 1.0f; + float halfHeight = 2.0f; + glm::vec3 center(0.0f); + CapsuleShape capsule(radius, halfHeight); + + { // simple test along xAxis + // toward capsule center + glm::vec3 rayStart(startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); + float delta = 2.0f * EPSILON; + + // over top cap + rayStart.y = halfHeight + radius + delta; + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // below bottom cap + rayStart.y = - halfHeight - radius - delta; + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // past edge of capsule cylindrical face + rayStart.y = 0.0f; + rayStart.z = radius + delta; + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + } + // TODO: test at steep angles near edge +} + +void ShapeColliderTests::rayHitsPlane() { + // make a simple plane + float planeDistanceFromOrigin = 3.579; + glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); + PlaneShape plane; + plane.setPosition(planePosition); + + // make a simple ray + float startDistance = 1.234f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); + + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + } + + float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; + float relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; + } + + // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setPosition(rotation * planePosition); + plane.setRotation(rotation); + rayStart = rotation * rayStart; + rayDirection = rotation * rayDirection; + + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + } + + expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; + relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; + } +} + +void ShapeColliderTests::rayMissesPlane() { + // make a simple plane + float planeDistanceFromOrigin = 3.579; + glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); + PlaneShape plane; + plane.setPosition(planePosition); + + { // parallel rays should miss + float startDistance = 1.234f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); + + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setPosition(rotation * planePosition); + plane.setRotation(rotation); + rayStart = rotation * rayStart; + rayDirection = rotation * rayDirection; + + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + } + + { // make a simple ray that points away from plane + float startDistance = 1.234f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); + + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setPosition(rotation * planePosition); + plane.setRotation(rotation); + rayStart = rotation * rayStart; + rayDirection = rotation * rayDirection; + + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + } +} void ShapeColliderTests::runAllTests() { sphereMissesSphere(); @@ -911,4 +1311,12 @@ void ShapeColliderTests::runAllTests() { sphereTouchesAACubeFaces(); sphereTouchesAACubeEdges(); sphereMissesAACube(); + + rayHitsSphere(); + rayBarelyHitsSphere(); + rayBarelyMissesSphere(); + rayHitsCapsule(); + rayMissesCapsule(); + rayHitsPlane(); + rayMissesPlane(); } diff --git a/tests/physics/src/ShapeColliderTests.h b/tests/physics/src/ShapeColliderTests.h index b51c48a61e..fd9f1f9706 100644 --- a/tests/physics/src/ShapeColliderTests.h +++ b/tests/physics/src/ShapeColliderTests.h @@ -27,6 +27,14 @@ namespace ShapeColliderTests { void sphereTouchesAACubeEdges(); void sphereMissesAACube(); + void rayHitsSphere(); + void rayBarelyHitsSphere(); + void rayBarelyMissesSphere(); + void rayHitsCapsule(); + void rayMissesCapsule(); + void rayHitsPlane(); + void rayMissesPlane(); + void runAllTests(); } From b37b9fb09745f6468904c22df80ae97b14bb3f76 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 11:59:26 -0700 Subject: [PATCH 93/99] remove warnings about unused variables --- interface/src/Menu.cpp | 2 -- interface/src/ui/ApplicationOverlay.cpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0dccf6256b..5c8c2e97aa 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1104,8 +1104,6 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson } else if (userResponse == QMessageBox::Open) { Application::getInstance()->getAvatar()->goToLocationFromAddress(placeData["address"].toObject()); } - - LocationManager* manager = reinterpret_cast(sender()); } void Menu::muteEnvironment() { diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 471ff20d66..af4648244f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -154,7 +154,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); @@ -221,8 +220,6 @@ void ApplicationOverlay::renderPointers() { Application* application = Application::getInstance(); // Render a crosshair over the mouse when in Oculus _numMagnifiers = 0; - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); //lazily load crosshair texture if (_crosshairTexture == 0) { From 3f3632564254fc5c1a164cfe04eb02a74aed739d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 11:59:37 -0700 Subject: [PATCH 94/99] remove warnings about signed/unsigned comparison --- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.cpp | 10 +++++----- domain-server/src/DomainServer.cpp | 2 +- libraries/audio/src/Sound.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index b3909660e2..d96fce450a 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -335,7 +335,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData(); // enumerate the ARBs attached to the otherNode and add all that should be added to mix - for (unsigned int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { + for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i]; if ((*otherNode != *node diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 7fb2a7dcab..9494e927a9 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -25,14 +25,14 @@ AudioMixerClientData::AudioMixerClientData() : } AudioMixerClientData::~AudioMixerClientData() { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { // delete this attached PositionalAudioRingBuffer delete _ringBuffers[i]; } } AvatarAudioRingBuffer* AudioMixerClientData::getAvatarAudioRingBuffer() const { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Microphone) { return (AvatarAudioRingBuffer*) _ringBuffers[i]; } @@ -79,7 +79,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector && ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) { matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i]; @@ -99,7 +99,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { } void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) { // this is a ring buffer that is ready to go // set its flag so we know to push its buffer when all is said and done @@ -113,7 +113,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam } void AudioMixerClientData::pushBuffersAfterFrameSend() { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = _ringBuffers[i]; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f7185063a9..d55a9b52ca 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -659,7 +659,7 @@ void DomainServer::readAvailableDatagrams() { wasNoisyTimerStarted = true; } - const quint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; + const qint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 9bc78f2303..7ef3afdf29 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -258,7 +258,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou // Now pull out the data quint32 outputAudioByteArraySize = qFromLittleEndian(dataHeader.descriptor.size); outputAudioByteArray.resize(outputAudioByteArraySize); - if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != outputAudioByteArraySize) { + if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) { qDebug() << "Error reading WAV file"; } From 9a3f8508cfaf3e7662a131f3d647a97b67ea5de2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 17 Jun 2014 12:12:53 -0700 Subject: [PATCH 95/99] add support for inside out ray intersection on AACube and AABox --- libraries/octree/src/AABox.cpp | 37 ++++++++++++++++++++++++++++++++ libraries/octree/src/AACube.cpp | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 7aa4d76134..409b362b24 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -180,6 +180,18 @@ static bool findIntersection(float origin, float direction, float corner, float return false; } +// finds the intersection between a ray and the inside facing plane on one axis +static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) { + if (direction > EPSILON) { + distance = -1.0f * (origin - (corner + size)) / direction; + return true; + } else if (direction < -EPSILON) { + distance = -1.0f * (origin - corner) / direction; + return true; + } + return false; +} + bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const { // handle the trivial cases where the expanded box contains the start or end if (expandedContains(start, expansion) || expandedContains(end, expansion)) { @@ -207,9 +219,34 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { // handle the trivial case where the box contains the origin if (contains(origin)) { + // We still want to calculate the distance from the origin to the inside out plane + float axisDistance; + if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { + distance = axisDistance; + face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + return true; + } + if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { + distance = axisDistance; + face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + return true; + } + if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) { + distance = axisDistance; + face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + return true; + } + // This case is unexpected, but mimics the previous behavior for inside out intersections distance = 0; return true; } + // check each axis float axisDistance; if ((findIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 && diff --git a/libraries/octree/src/AACube.cpp b/libraries/octree/src/AACube.cpp index 443d725a38..5a8839db4e 100644 --- a/libraries/octree/src/AACube.cpp +++ b/libraries/octree/src/AACube.cpp @@ -169,6 +169,18 @@ static bool findIntersection(float origin, float direction, float corner, float return false; } +// finds the intersection between a ray and the inside facing plane on one axis +static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) { + if (direction > EPSILON) { + distance = -1.0f * (origin - (corner + size)) / direction; + return true; + } else if (direction < -EPSILON) { + distance = -1.0f * (origin - corner) / direction; + return true; + } + return false; +} + bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const { // handle the trivial cases where the expanded box contains the start or end if (expandedContains(start, expansion) || expandedContains(end, expansion)) { @@ -196,9 +208,35 @@ bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { // handle the trivial case where the box contains the origin if (contains(origin)) { + + // We still want to calculate the distance from the origin to the inside out plane + float axisDistance; + if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { + distance = axisDistance; + face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + return true; + } + if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { + distance = axisDistance; + face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + return true; + } + if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) { + distance = axisDistance; + face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + return true; + } + // This case is unexpected, but mimics the previous behavior for inside out intersections distance = 0; return true; } + // check each axis float axisDistance; if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 && From f18864bd72019f7b69a8d13b8aaa2d505237f721 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 12:36:36 -0700 Subject: [PATCH 96/99] Moved findRayIntersection() to the Shape classes --- libraries/shared/src/CapsuleShape.cpp | 10 ++++ libraries/shared/src/CapsuleShape.h | 2 + libraries/shared/src/ListShape.h | 3 ++ libraries/shared/src/PlaneShape.cpp | 17 +++++++ libraries/shared/src/PlaneShape.h | 2 + libraries/shared/src/Shape.h | 2 + libraries/shared/src/ShapeCollider.cpp | 64 +----------------------- libraries/shared/src/ShapeCollider.h | 7 --- libraries/shared/src/SphereShape.h | 2 + tests/physics/src/ShapeColliderTests.cpp | 44 ++++++++-------- 10 files changed, 61 insertions(+), 92 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 8e887107dc..5416ff92a6 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -13,6 +13,8 @@ #include #include "CapsuleShape.h" + +#include "GeometryUtil.h" #include "SharedUtil.h" @@ -84,3 +86,11 @@ void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& en updateBoundingRadius(); } +bool CapsuleShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { + glm::vec3 capsuleStart, capsuleEnd; + getStartPoint(capsuleStart); + getEndPoint(capsuleEnd); + // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. + // TODO: implement the raycast to return inside surface intersection for the internal rayStart. + return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, _radius, distance); +} diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 756ae18911..fdd6c3eda6 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -39,6 +39,8 @@ public: void setRadiusAndHalfHeight(float radius, float height); void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + protected: void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; } diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h index 7ba2410a23..17e7d7b2b6 100644 --- a/libraries/shared/src/ListShape.h +++ b/libraries/shared/src/ListShape.h @@ -55,6 +55,9 @@ public: void setShapes(QVector& shapes); + // TODO: either implement this or remove ListShape altogether + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { return false; } + protected: void clear(); void computeBoundingRadius(); diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index 0617feb863..e9563c6d8b 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -38,3 +38,20 @@ glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position)); } + +bool PlaneShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { + glm::vec3 n = getNormal(); + float denominator = glm::dot(n, rayDirection); + if (fabsf(denominator) < EPSILON) { + // line is parallel to plane + return glm::dot(_position - rayStart, n) < EPSILON; + } else { + float d = glm::dot(_position - rayStart, n) / denominator; + if (d > 0.0f) { + // ray points toward plane + distance = d; + return true; + } + } + return false; +} diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h index 24a3f1a2bc..b8a93324b7 100644 --- a/libraries/shared/src/PlaneShape.h +++ b/libraries/shared/src/PlaneShape.h @@ -20,6 +20,8 @@ public: glm::vec3 getNormal() const; glm::vec4 getCoefficients() const; + + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; }; #endif // hifi_PlaneShape_h diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 87b84ea73b..3926f6cd07 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -38,6 +38,8 @@ public: virtual void setPosition(const glm::vec3& position) { _position = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } + virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0; + protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _boundingRadius(0.f), _position(0.f), _rotation() {} diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 24d7fdb01a..bbedeb401d 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -772,7 +772,7 @@ bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3 Shape* shape = shapes.at(i); if (shape) { float distance; - if (findRayIntersectionWithShape(shape, rayStart, rayDirection, distance)) { + if (shape->findRayIntersection(rayStart, rayDirection, distance)) { if (distance < hitDistance) { hitDistance = distance; } @@ -785,66 +785,4 @@ bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3 return false; } -bool findRayIntersectionWithShape(const Shape* shape, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) { - // NOTE: rayDirection is assumed to be normalized - int typeA = shape->getType(); - if (typeA == Shape::SPHERE_SHAPE) { - const SphereShape* sphere = static_cast(shape); - glm::vec3 sphereCenter = sphere->getPosition(); - float r2 = sphere->getRadius() * sphere->getRadius(); // r2 = radius^2 - - // compute closest approach (CA) - float a = glm::dot(sphere->getPosition() - rayStart, rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(sphereCenter, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA - if (b2 > r2) { - // ray does not hit sphere - return false; - } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection - float d2 = glm::distance2(rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start - if (a < 0.0f) { - // ray points away from sphere-center - if (d2 > r2) { - // ray starts outside sphere - return false; - } - // ray starts inside sphere - distance = c + a; - } else if (d2 > r2) { - // ray starts outside sphere - distance = a - c; - } else { - // ray starts inside sphere - distance = a + c; - } - return true; - } else if (typeA == Shape::CAPSULE_SHAPE) { - const CapsuleShape* capsule = static_cast(shape); - float radius = capsule->getRadius(); - glm::vec3 capsuleStart, capsuleEnd; - capsule->getStartPoint(capsuleStart); - capsule->getEndPoint(capsuleEnd); - // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. - // TODO: implement the raycast to return inside surface intersection for the internal rayStart. - return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, radius, distance); - } else if (typeA == Shape::PLANE_SHAPE) { - const PlaneShape* plane = static_cast(shape); - glm::vec3 n = plane->getNormal(); - glm::vec3 P = plane->getPosition(); - float denominator = glm::dot(n, rayDirection); - if (fabsf(denominator) < EPSILON) { - // line is parallel to plane - return glm::dot(P - rayStart, n) < EPSILON; - } else { - float d = glm::dot(P - rayStart, n) / denominator; - if (d > 0.0f) { - // ray points toward plane - distance = d; - return true; - } - } - } - return false; -} - } // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 308a8cf10b..8261aceaf3 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -157,13 +157,6 @@ namespace ShapeCollider { /// \return true if ray hits any shape in shapes bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& startPoint, const glm::vec3& direction, float& minDistance); - /// \param shapeA pointer to shape (cannot be NULL) - /// \param startPoint beginning of ray - /// \param direction direction of ray - /// \param distance[out] distance to intersection of shape and ray - /// \return true if ray hits shapeA - bool findRayIntersectionWithShape(const Shape* shapeA, const glm::vec3& startPoint, const glm::vec3& direction, float& distance); - } // namespace ShapeCollider #endif // hifi_ShapeCollider_h diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index 62783ab340..e87b8acab1 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -29,6 +29,8 @@ public: float getRadius() const { return _boundingRadius; } void setRadius(float radius) { _boundingRadius = radius; } + + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; }; #endif // hifi_SphereShape_h diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 3387ba6aba..608e012998 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -911,7 +911,7 @@ void ShapeColliderTests::rayHitsSphere() { // very simple ray along xAxis { float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } @@ -928,7 +928,7 @@ void ShapeColliderTests::rayHitsSphere() { rayDirection = - glm::normalize(rayStart); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } @@ -958,7 +958,7 @@ void ShapeColliderTests::rayHitsSphere() { sphere.setPosition(rotation * translation); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } @@ -983,7 +983,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { // very simple ray along xAxis float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; } @@ -998,7 +998,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { // ...and test again distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; } } @@ -1018,7 +1018,7 @@ void ShapeColliderTests::rayBarelyMissesSphere() { // very simple ray along xAxis float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } if (distance != FLT_MAX) { @@ -1036,7 +1036,7 @@ void ShapeColliderTests::rayBarelyMissesSphere() { // ...and test again distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } if (distance != FLT_MAX) { @@ -1056,7 +1056,7 @@ void ShapeColliderTests::rayHitsCapsule() { glm::vec3 rayStart(startDistance, 0.0f, 0.0f); glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } float expectedDistance = startDistance - radius; @@ -1068,7 +1068,7 @@ void ShapeColliderTests::rayHitsCapsule() { // toward top of cylindrical wall rayStart.y = halfHeight; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } relativeError = fabsf(distance - expectedDistance) / startDistance; @@ -1080,7 +1080,7 @@ void ShapeColliderTests::rayHitsCapsule() { float delta = 2.0f * EPSILON; rayStart.y = halfHeight + delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } relativeError = fabsf(distance - expectedDistance) / startDistance; @@ -1093,7 +1093,7 @@ void ShapeColliderTests::rayHitsCapsule() { // toward tip of top cap rayStart.y = halfHeight + radius - delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine @@ -1106,7 +1106,7 @@ void ShapeColliderTests::rayHitsCapsule() { // toward tip of bottom cap rayStart.y = - halfHeight - radius + delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine @@ -1120,7 +1120,7 @@ void ShapeColliderTests::rayHitsCapsule() { rayStart.y = 0.0f; rayStart.z = radius - delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine @@ -1150,7 +1150,7 @@ void ShapeColliderTests::rayMissesCapsule() { // over top cap rayStart.y = halfHeight + radius + delta; float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { @@ -1160,7 +1160,7 @@ void ShapeColliderTests::rayMissesCapsule() { // below bottom cap rayStart.y = - halfHeight - radius - delta; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { @@ -1171,7 +1171,7 @@ void ShapeColliderTests::rayMissesCapsule() { rayStart.y = 0.0f; rayStart.z = radius + delta; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { @@ -1194,7 +1194,7 @@ void ShapeColliderTests::rayHitsPlane() { glm::vec3 rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (!plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; } @@ -1215,7 +1215,7 @@ void ShapeColliderTests::rayHitsPlane() { rayDirection = rotation * rayDirection; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (!plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; } @@ -1239,7 +1239,7 @@ void ShapeColliderTests::rayMissesPlane() { glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { @@ -1257,7 +1257,7 @@ void ShapeColliderTests::rayMissesPlane() { rayDirection = rotation * rayDirection; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { @@ -1271,7 +1271,7 @@ void ShapeColliderTests::rayMissesPlane() { glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { @@ -1289,7 +1289,7 @@ void ShapeColliderTests::rayMissesPlane() { rayDirection = rotation * rayDirection; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { From b6cecf3cfd4ef651f1fcc8120dae7880265b37e7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 12:50:49 -0700 Subject: [PATCH 97/99] Add SphereShape.cpp to project --- libraries/shared/src/SphereShape.cpp | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 libraries/shared/src/SphereShape.cpp diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp new file mode 100644 index 0000000000..49137fac43 --- /dev/null +++ b/libraries/shared/src/SphereShape.cpp @@ -0,0 +1,44 @@ +// +// SphereShape.cpp +// libraries/shared/src +// +// Created by Andrew Meadows on 2014.06.17 +// 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 + +#include "SphereShape.h" + +bool SphereShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { + float r2 = _boundingRadius * _boundingRadius; + + // compute closest approach (CA) + float a = glm::dot(_position - rayStart, rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(_position, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA + if (b2 > r2) { + // ray does not hit sphere + return false; + } + float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection + float d2 = glm::distance2(rayStart, _position); // d2 = squared distance from sphere-center to ray-start + if (a < 0.0f) { + // ray points away from sphere-center + if (d2 > r2) { + // ray starts outside sphere + return false; + } + // ray starts inside sphere + distance = c + a; + } else if (d2 > r2) { + // ray starts outside sphere + distance = a - c; + } else { + // ray starts inside sphere + distance = a + c; + } + return true; +} From 4413049302d1a308bbb4870c4d8dff163e18402b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 17 Jun 2014 13:05:35 -0700 Subject: [PATCH 98/99] fixed a bug in inside out ray casting returning the wrong face --- libraries/octree/src/AABox.cpp | 6 +- libraries/octree/src/AACube.cpp | 6 +- tests/octree/src/AABoxCubeTests.cpp | 100 ++++++++++++++++++++++++++++ tests/octree/src/AABoxCubeTests.h | 20 ++++++ tests/octree/src/OctreeTests.cpp | 2 +- tests/octree/src/OctreeTests.h | 2 +- tests/octree/src/main.cpp | 2 + 7 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 tests/octree/src/AABoxCubeTests.cpp create mode 100644 tests/octree/src/AABoxCubeTests.h diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 409b362b24..60f26a5533 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -225,21 +225,21 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; return true; } if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; return true; } if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 && isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) { distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; return true; } // This case is unexpected, but mimics the previous behavior for inside out intersections diff --git a/libraries/octree/src/AACube.cpp b/libraries/octree/src/AACube.cpp index 5a8839db4e..e359eac9e9 100644 --- a/libraries/octree/src/AACube.cpp +++ b/libraries/octree/src/AACube.cpp @@ -215,21 +215,21 @@ bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; return true; } if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; return true; } if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 && isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) { distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; return true; } // This case is unexpected, but mimics the previous behavior for inside out intersections diff --git a/tests/octree/src/AABoxCubeTests.cpp b/tests/octree/src/AABoxCubeTests.cpp new file mode 100644 index 0000000000..85787e279b --- /dev/null +++ b/tests/octree/src/AABoxCubeTests.cpp @@ -0,0 +1,100 @@ +// +// AABoxCubeTests.h +// tests/octree/src +// +// Created by Brad Hefta-Gaub on 06/04/2014. +// 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 + +#include +#include + +#include "AABoxCubeTests.h" + +void AABoxCubeTests::AABoxCubeTests() { + qDebug() << "******************************************************************************************"; + qDebug() << "AABoxCubeTests::AABoxCubeTests()"; + + { + qDebug() << "Test 1: AABox.findRayIntersection() inside out MIN_X_FACE"; + + glm::vec3 corner(0.0f, 0.0f, 0.0f); + float size = 1.0f; + + AABox box(corner, size); + glm::vec3 origin(0.5f, 0.5f, 0.5f); + glm::vec3 direction(-1.0f, 0.0f, 0.0f); + float distance; + BoxFace face; + + bool intersects = box.findRayIntersection(origin, direction, distance, face); + + if (intersects && distance == 0.5f && face == MIN_X_FACE) { + qDebug() << "Test 1: PASSED"; + } else { + qDebug() << "intersects=" << intersects << "expected=" << true; + qDebug() << "distance=" << distance << "expected=" << 0.5f; + qDebug() << "face=" << face << "expected=" << MIN_X_FACE; + + } + } + + { + qDebug() << "Test 2: AABox.findRayIntersection() inside out MAX_X_FACE"; + + glm::vec3 corner(0.0f, 0.0f, 0.0f); + float size = 1.0f; + + AABox box(corner, size); + glm::vec3 origin(0.5f, 0.5f, 0.5f); + glm::vec3 direction(1.0f, 0.0f, 0.0f); + float distance; + BoxFace face; + + bool intersects = box.findRayIntersection(origin, direction, distance, face); + + if (intersects && distance == 0.5f && face == MAX_X_FACE) { + qDebug() << "Test 2: PASSED"; + } else { + qDebug() << "intersects=" << intersects << "expected=" << true; + qDebug() << "distance=" << distance << "expected=" << 0.5f; + qDebug() << "face=" << face << "expected=" << MAX_X_FACE; + + } + } + + { + qDebug() << "Test 3: AABox.findRayIntersection() outside in"; + + glm::vec3 corner(0.5f, 0.0f, 0.0f); + float size = 0.5f; + + AABox box(corner, size); + glm::vec3 origin(0.25f, 0.25f, 0.25f); + glm::vec3 direction(1.0f, 0.0f, 0.0f); + float distance; + BoxFace face; + + bool intersects = box.findRayIntersection(origin, direction, distance, face); + + if (intersects && distance == 0.25f && face == MIN_X_FACE) { + qDebug() << "Test 3: PASSED"; + } else { + qDebug() << "intersects=" << intersects << "expected=" << true; + qDebug() << "distance=" << distance << "expected=" << 0.5f; + qDebug() << "face=" << face << "expected=" << MIN_X_FACE; + + } + } + + qDebug() << "******************************************************************************************"; +} + +void AABoxCubeTests::runAllTests() { + AABoxCubeTests(); +} diff --git a/tests/octree/src/AABoxCubeTests.h b/tests/octree/src/AABoxCubeTests.h new file mode 100644 index 0000000000..8d1ece51f8 --- /dev/null +++ b/tests/octree/src/AABoxCubeTests.h @@ -0,0 +1,20 @@ +// +// AABoxCubeTests.h +// tests/octree/src +// +// Created by Brad Hefta-Gaub on 06/04/2014. +// 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_AABoxCubeTests_h +#define hifi_AABoxCubeTests_h + +namespace AABoxCubeTests { + void AABoxCubeTests(); + void runAllTests(); +} + +#endif // hifi_AABoxCubeTests_h diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index ddc3f2c74d..70ef97f225 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -1,6 +1,6 @@ // // OctreeTests.h -// tests/physics/src +// tests/octree/src // // Created by Brad Hefta-Gaub on 06/04/2014. // Copyright 2014 High Fidelity, Inc. diff --git a/tests/octree/src/OctreeTests.h b/tests/octree/src/OctreeTests.h index 53b0d9fb83..5eae6c6158 100644 --- a/tests/octree/src/OctreeTests.h +++ b/tests/octree/src/OctreeTests.h @@ -1,6 +1,6 @@ // // OctreeTests.h -// tests/physics/src +// tests/octree/src // // Created by Brad Hefta-Gaub on 06/04/2014. // Copyright 2014 High Fidelity, Inc. diff --git a/tests/octree/src/main.cpp b/tests/octree/src/main.cpp index ec3dc19e01..de7b3926ae 100644 --- a/tests/octree/src/main.cpp +++ b/tests/octree/src/main.cpp @@ -9,8 +9,10 @@ // #include "OctreeTests.h" +#include "AABoxCubeTests.h" int main(int argc, char** argv) { OctreeTests::runAllTests(); + AABoxCubeTests::runAllTests(); return 0; } From 89fbeb0b6d42e9a7c61b524c8080ad4f70161722 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 17 Jun 2014 13:09:48 -0700 Subject: [PATCH 99/99] Fix invalid remote scripts being loaded --- libraries/script-engine/src/ScriptEngine.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a4aae61248..b0cce114a9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -147,7 +147,12 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); - _scriptContents = reply->readAll(); + if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { + _scriptContents = reply->readAll(); + } else { + qDebug() << "ERROR Loading file:" << url.toString(); + emit errorMessage("ERROR Loading file:" + url.toString()); + } } } }