From a67153caff11f0b6f147be22040020eb45c92853 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 17:14:57 -0700 Subject: [PATCH 01/38] Fix for bug with reliable messages. --- interface/interface_en.ts | 8 ++++---- libraries/metavoxels/src/DatagramSequencer.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 175a1f4525..586d70e66f 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 052f480fcf..5c605dc0f4 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -420,7 +420,7 @@ bool CircularBuffer::atEnd() const { } qint64 CircularBuffer::bytesAvailable() const { - return _size - _offset + QIODevice::bytesAvailable(); + return _size - _offset; } bool CircularBuffer::canReadLine() const { From 612a83245c28e84c72660e699975013f42996c78 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Mar 2014 15:18:51 -0700 Subject: [PATCH 02/38] Updated translations. --- interface/interface_en.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 586d70e66f..f07f50f287 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) @@ -27,25 +27,25 @@ ChatWindow - - + + Chat - - + + Connecting to XMPP... - - + + online now: - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file From 35f57cb998ac804d085fd26034af7e77836be35c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Mar 2014 18:22:56 -0700 Subject: [PATCH 03/38] Working on streaming metadata. --- libraries/metavoxels/src/Bitstream.cpp | 3 +- libraries/metavoxels/src/Bitstream.h | 61 +++++++++++-------- .../metavoxels/src/MetavoxelMessages.cpp | 2 +- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index f80ef3aa28..1b89acd18f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -90,11 +90,12 @@ QList Bitstream::getMetaObjectSubClasses(const QMetaObject* return getMetaObjectSubClasses().values(metaObject); } -Bitstream::Bitstream(QDataStream& underlying, QObject* parent) : +Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, QObject* parent) : QObject(parent), _underlying(underlying), _byte(0), _position(0), + _metadataType(metadataType), _metaObjectStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index ca26509d60..1acdd63841 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -55,26 +55,26 @@ private: /// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When /// subsequently streamed, only the ID is sent. -template class RepeatedValueStreamer { +template class RepeatedValueStreamer { public: RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), _lastPersistentID(0), _lastTransientOffset(0) { } - QHash getAndResetTransientOffsets(); + QHash getAndResetTransientOffsets(); - void persistTransientOffsets(const QHash& transientOffsets); + void persistTransientOffsets(const QHash& transientOffsets); - QHash getAndResetTransientValues(); + QHash getAndResetTransientValues(); - void persistTransientValues(const QHash& transientValues); + void persistTransientValues(const QHash& transientValues); int takePersistentID(P value) { return _persistentIDs.take(value); } - T takePersistentValue(int id) { T value = _persistentValues.take(id); _persistentIDs.remove(value); return value; } + V takePersistentValue(int id) { V value = _persistentValues.take(id); _valueIDs.remove(value); return value; } - RepeatedValueStreamer& operator<<(T value); - RepeatedValueStreamer& operator>>(T& value); + RepeatedValueStreamer& operator<<(K value); + RepeatedValueStreamer& operator>>(V& value); private: @@ -83,23 +83,24 @@ private: int _lastPersistentID; int _lastTransientOffset; QHash _persistentIDs; - QHash _transientOffsets; - QHash _persistentValues; - QHash _transientValues; + QHash _transientOffsets; + QHash _persistentValues; + QHash _transientValues; + QHash _valueIDs; }; -template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { - QHash transientOffsets; +template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { + QHash transientOffsets; _transientOffsets.swap(transientOffsets); _lastTransientOffset = 0; _idStreamer.setBitsFromValue(_lastPersistentID); return transientOffsets; } -template inline void RepeatedValueStreamer::persistTransientOffsets( - const QHash& transientOffsets) { +template inline void RepeatedValueStreamer::persistTransientOffsets( + const QHash& transientOffsets) { int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { + for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { int& id = _persistentIDs[it.key()]; if (id == 0) { id = oldLastPersistentID + it.value(); @@ -109,18 +110,18 @@ template inline void RepeatedValueStreamer::persistTrans _idStreamer.setBitsFromValue(_lastPersistentID); } -template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { - QHash transientValues; +template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { + QHash transientValues; _transientValues.swap(transientValues); _idStreamer.setBitsFromValue(_lastPersistentID); return transientValues; } -template inline void RepeatedValueStreamer::persistTransientValues( - const QHash& transientValues) { +template inline void RepeatedValueStreamer::persistTransientValues( + const QHash& transientValues) { int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { - int& id = _persistentIDs[it.value()]; + for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { + int& id = _valueIDs[it.value()]; if (id == 0) { id = oldLastPersistentID + it.key(); _lastPersistentID = qMax(_lastPersistentID, id); @@ -130,7 +131,8 @@ template inline void RepeatedValueStreamer::persistTrans _idStreamer.setBitsFromValue(_lastPersistentID); } -template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(T value) { +template inline RepeatedValueStreamer& + RepeatedValueStreamer::operator<<(K value) { int id = _persistentIDs.value(value); if (id == 0) { int& offset = _transientOffsets[value]; @@ -147,7 +149,8 @@ template inline RepeatedValueStreamer& RepeatedValueStre return *this; } -template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(T& value) { +template inline RepeatedValueStreamer& + RepeatedValueStreamer::operator>>(V& value) { int id; _idStreamer >> id; if (id <= _lastPersistentID) { @@ -155,7 +158,7 @@ template inline RepeatedValueStreamer& RepeatedValueStre } else { int offset = id - _lastPersistentID; - typename QHash::iterator it = _transientValues.find(offset); + typename QHash::iterator it = _transientValues.find(offset); if (it == _transientValues.end()) { _stream > value; _transientValues.insert(offset, value); @@ -205,8 +208,10 @@ public: /// Returns the list of registered subclasses for the supplied meta-object. static QList getMetaObjectSubClasses(const QMetaObject* metaObject); + enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA }; + /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. - Bitstream(QDataStream& underlying, QObject* parent = NULL); + Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, QObject* parent = NULL); /// Writes a set of bits to the underlying stream. /// \param bits the number of bits to write @@ -339,7 +344,9 @@ private: quint8 _byte; int _position; - RepeatedValueStreamer _metaObjectStreamer; + MetadataType _metadataType; + + RepeatedValueStreamer _metaObjectStreamer; RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index ca37746780..7a60ae6263 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -143,7 +143,7 @@ private: }; SetSpannerEditVisitor::SetSpannerEditVisitor(Spanner* spanner) : - MetavoxelVisitor(QVector(), spanner->getAttributes()), + MetavoxelVisitor(spanner->getAttributes(), spanner->getAttributes()), _spanner(spanner) { } From b21247ca67457e22d77e8b51ab63d2477a4635ad Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 16 Mar 2014 17:29:30 -0700 Subject: [PATCH 04/38] Optional hash/full metadata streaming for QObjects. --- libraries/metavoxels/src/Bitstream.cpp | 194 +++++++++++++++++++----- libraries/metavoxels/src/Bitstream.h | 55 ++++++- tests/metavoxels/src/MetavoxelTests.cpp | 34 ++++- 3 files changed, 243 insertions(+), 40 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 1b89acd18f..6c8d8ec559 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -8,8 +8,8 @@ #include +#include #include -#include #include #include #include @@ -377,13 +377,9 @@ Bitstream& Bitstream::operator<<(const QObject* object) { } Bitstream& Bitstream::operator>>(QObject*& object) { - const QMetaObject* metaObject; - _metaObjectStreamer >> metaObject; - if (!metaObject) { - object = NULL; - return *this; - } - readProperties(object = metaObject->newInstance()); + ObjectReader objectReader; + _metaObjectStreamer >> objectReader; + object = objectReader.read(*this); return *this; } @@ -393,7 +389,14 @@ Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { } Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { - _metaObjectStreamer >> metaObject; + ObjectReader objectReader; + _metaObjectStreamer >> objectReader; + metaObject = objectReader.getMetaObject(); + return *this; +} + +Bitstream& Bitstream::operator>>(ObjectReader& objectReader) { + _metaObjectStreamer >> objectReader; return *this; } @@ -438,21 +441,111 @@ Bitstream& Bitstream::operator>>(SharedObjectPointer& object) { } Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { - return *this << (metaObject ? QByteArray::fromRawData(metaObject->className(), - strlen(metaObject->className())) : QByteArray()); + if (!metaObject) { + return *this << QByteArray(); + } + *this << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className())); + if (_metadataType == NO_METADATA) { + return *this; + } + int storedPropertyCount = 0; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored() && getTypeStreamers().contains(property.userType())) { + storedPropertyCount++; + } + } + *this << storedPropertyCount; + QCryptographicHash hash(QCryptographicHash::Md5); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); + if (!typeStreamer) { + continue; + } + _typeStreamerStreamer << typeStreamer; + 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); + } + return *this; } -Bitstream& Bitstream::operator>(const QMetaObject*& metaObject) { +Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QByteArray className; *this >> className; if (className.isEmpty()) { - metaObject = NULL; + objectReader = ObjectReader(); return *this; } - metaObject = getMetaObjects().value(className); + const QMetaObject* metaObject = getMetaObjects().value(className); if (!metaObject) { qWarning() << "Unknown class name: " << className << "\n"; } + if (_metadataType == NO_METADATA) { + objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject)); + return *this; + } + int storedPropertyCount; + *this >> storedPropertyCount; + QVector properties(storedPropertyCount); + for (int i = 0; i < storedPropertyCount; i++) { + const TypeStreamer* typeStreamer; + *this >> typeStreamer; + QMetaProperty property = QMetaProperty(); + if (_metadataType == FULL_METADATA) { + QByteArray propertyName; + *this >> propertyName; + if (metaObject) { + property = metaObject->property(metaObject->indexOfProperty(propertyName)); + } + } + 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) { + QCryptographicHash hash(QCryptographicHash::Md5); + if (metaObject) { + int propertyIndex = 0; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); + if (!typeStreamer) { + continue; + } + if (propertyIndex >= properties.size() || properties.at(propertyIndex).getStreamer() != typeStreamer) { + objectReader = ObjectReader(className, metaObject, properties); + return *this; + } + hash.addData(property.name(), strlen(property.name()) + 1); + propertyIndex++; + } + if (propertyIndex != properties.size()) { + objectReader = ObjectReader(className, metaObject, properties); + return *this; + } + } + QByteArray localHashResult = hash.result(); + QByteArray remoteHashResult(localHashResult.size(), 0); + read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); + if (metaObject && localHashResult == remoteHashResult) { + objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject)); + return *this; + } + } + objectReader = ObjectReader(className, metaObject, properties); return *this; } @@ -509,12 +602,9 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { - const QMetaObject* metaObject; - _metaObjectStreamer >> metaObject; - if (metaObject != pointer->metaObject()) { - qWarning() << "Class mismatch: " << pointer->metaObject()->className() << metaObject->className(); - } - readProperties(pointer.data()); + ObjectReader objectReader; + _metaObjectStreamer >> objectReader; + objectReader.read(*this, pointer.data()); } else { QObject* rawObject; @@ -533,20 +623,6 @@ void Bitstream::clearSharedObject(QObject* object) { } } -void Bitstream::readProperties(QObject* object) { - const QMetaObject* metaObject = object->metaObject(); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored(object)) { - continue; - } - const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); - if (streamer) { - property.write(object, streamer->read(*this)); - } - } -} - QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; @@ -562,3 +638,53 @@ QHash& Bitstream::getTypeStreamers() { return typeStreamers; } +QVector Bitstream::getPropertyReaders(const QMetaObject* metaObject) { + QVector propertyReaders; + if (!metaObject) { + return propertyReaders; + } + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); + if (typeStreamer) { + propertyReaders.append(PropertyReader(typeStreamer, property)); + } + } + return propertyReaders; +} + +ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, + const QVector& 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; +} + +uint qHash(const ObjectReader& objectReader, uint seed) { + return qHash(objectReader.getClassName(), seed); +} + +PropertyReader::PropertyReader(const TypeStreamer* 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); + } +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 1acdd63841..8afea69e4c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -10,6 +10,7 @@ #define __interface__Bitstream__ #include +#include #include #include #include @@ -29,7 +30,9 @@ class QUrl; class Attribute; class AttributeValue; class Bitstream; +class ObjectReader; class OwnedAttributeValue; +class PropertyReader; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -187,7 +190,7 @@ public: class ReadMappings { public: - QHash metaObjectValues; + QHash metaObjectValues; QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; @@ -300,6 +303,7 @@ public: Bitstream& operator<<(const QMetaObject* metaObject); Bitstream& operator>>(const QMetaObject*& metaObject); + Bitstream& operator>>(ObjectReader& objectReader); Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); @@ -314,7 +318,7 @@ public: Bitstream& operator>>(SharedObjectPointer& object); Bitstream& operator<(const QMetaObject* metaObject); - Bitstream& operator>(const QMetaObject*& metaObject); + Bitstream& operator>(ObjectReader& objectReader); Bitstream& operator<(const TypeStreamer* streamer); Bitstream& operator>(const TypeStreamer*& streamer); @@ -338,15 +342,13 @@ private slots: private: - void readProperties(QObject* object); - QDataStream& _underlying; quint8 _byte; int _position; MetadataType _metadataType; - RepeatedValueStreamer _metaObjectStreamer; + RepeatedValueStreamer _metaObjectStreamer; RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; @@ -357,6 +359,7 @@ private: static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); + static QVector getPropertyReaders(const QMetaObject* metaObject); }; template inline Bitstream& Bitstream::operator<<(const QList& list) { @@ -425,6 +428,48 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +/// 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; } + + bool isNull() const { return _className.isEmpty(); } + + QObject* read(Bitstream& in, 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); + +/// Contains the information required to read an object property from the stream and apply it. +class PropertyReader { +public: + + PropertyReader(const TypeStreamer* streamer = NULL, const QMetaProperty& property = QMetaProperty()); + + const TypeStreamer* getStreamer() const { return _streamer; } + + void read(Bitstream& in, QObject* object) const; + +private: + + const TypeStreamer* _streamer; + QMetaProperty _property; +}; + Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index b1f3315bc0..9de48364b6 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -34,9 +34,33 @@ static int streamedBytesReceived = 0; static int sharedObjectsCreated = 0; static int sharedObjectsDestroyed = 0; +static bool testSerialization(Bitstream::MetadataType metadataType) { + QByteArray array; + QDataStream outStream(&array, QIODevice::WriteOnly); + Bitstream out(outStream, metadataType); + SharedObjectPointer testObjectWritten = new TestSharedObjectA(randFloat()); + out << testObjectWritten; + out.flush(); + + QDataStream inStream(array); + Bitstream in(inStream, metadataType); + SharedObjectPointer testObjectRead; + in >> testObjectRead; + + if (!testObjectWritten->equals(testObjectRead)) { + QDebug debug = qDebug() << "Read/write mismatch"; + testObjectWritten->dump(debug); + testObjectRead->dump(debug); + return true; + } + + return false; +} + bool MetavoxelTests::run() { - qDebug() << "Running metavoxel tests..."; + qDebug() << "Running transmission tests..."; + qDebug(); // seed the random number generator so that our tests are reproducible srand(0xBAAAAABE); @@ -62,6 +86,14 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived; qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; + qDebug(); + + qDebug() << "Running serialization tests..."; + qDebug(); + + if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) { + return true; + } qDebug() << "All tests passed!"; From 95410d2b66efdb220e87a15f5950e9cb69c1fbf9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 16 Mar 2014 18:04:37 -0700 Subject: [PATCH 05/38] Cleaned up the preprocessor definitions. --- libraries/metavoxels/src/Bitstream.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 8afea69e4c..f4e8d19ce3 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -502,13 +502,8 @@ public: #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); -#ifdef WIN32 -#define _Pragma __pragma -#endif - /// Declares the metatype and the streaming operators. The last lines /// ensure that the generated file will be included in the link phase. -#define STRINGIFY(x) #x #ifdef _WIN32 #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ From 61660f890e7a680c1498ee6d062f35d2f67502d2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Mar 2014 11:39:16 -0700 Subject: [PATCH 06/38] Working on metadata for metatypes. --- .../metavoxels/src/AttributeRegistry.cpp | 3 + libraries/metavoxels/src/AttributeRegistry.h | 4 ++ libraries/metavoxels/src/Bitstream.h | 6 +- tools/mtc/src/main.cpp | 64 +++++++++++++++---- 4 files changed, 62 insertions(+), 15 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index ccb6d3970e..634e7d3b57 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -17,6 +17,9 @@ REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) +static int attributePointerMetaTypeId = qRegisterMetaType(); +static int ownedAttributeValueMetaTypeId = qRegisterMetaType(); + AttributeRegistry* AttributeRegistry::getInstance() { static AttributeRegistry registry; return ®istry; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index b6a415bef7..f3dd9f4632 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -29,6 +29,8 @@ class MetavoxelStreamState; typedef SharedObjectPointerTemplate AttributePointer; +Q_DECLARE_METATYPE(AttributePointer) + /// Maintains information about metavoxel attribute types. class AttributeRegistry { public: @@ -150,6 +152,8 @@ public: OwnedAttributeValue& operator=(const OwnedAttributeValue& other); }; +Q_DECLARE_METATYPE(OwnedAttributeValue) + /// Represents a registered attribute. class Attribute : public SharedObject { Q_OBJECT diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index f4e8d19ce3..f52db029b6 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -537,7 +537,11 @@ template int registerStreamableMetaType() { } /// Flags a class as streamable (use as you would Q_OBJECT). -#define STREAMABLE public: static const int Type; private: +#define STREAMABLE public: \ + static const int Type; \ + static int getFieldCount(); \ + void setFieldValue(int index, const QVariant& value); \ + private: /// Flags a field or base class as streaming. #define STREAM diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 248c2ddd2d..598b7b10d0 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -24,10 +24,16 @@ public: QStringList bases; }; +class Field { +public: + QString type; + QString name; +}; + class Streamable { public: Class clazz; - QStringList fields; + QList fields; }; void processInput(QTextStream& in, QList* streamables) { @@ -66,8 +72,10 @@ void processInput(QTextStream& in, QList* streamables) { } else if (match.startsWith("STREAM")) { match.chop(1); // get rid of the semicolon - match = match.trimmed(); // and any space before it - currentStreamable.fields.append(match.mid(match.lastIndexOf(' ') + 1)); + match = match.mid(match.indexOf(' ') + 1).trimmed(); // and STREAM, and any space before it + int index = match.lastIndexOf(' '); + Field field = { match.left(index).simplified(), match.mid(index + 1) }; + currentStreamable.fields.append(field); } else { // match.startsWith("class") classExp.exactMatch(match); @@ -90,12 +98,42 @@ void generateOutput (QTextStream& out, const QList& streamables) { foreach (const Streamable& str, streamables) { const QString& name = str.clazz.name; + out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n"; + + out << "int " << name << "::getFieldCount() {\n"; + out << " return " << str.fields.size(); + foreach(const QString& base, str.clazz.bases) { + out << " + " << base << "::getFieldCount()"; + } + out << ";\n"; + out << "}\n"; + + out << "void " << name << "::setFieldValue(int index, const QVariant& value) {\n"; + if (!str.clazz.bases.isEmpty()) { + out << " int nextIndex;\n"; + } + foreach (const QString& base, str.clazz.bases) { + out << " if ((nextIndex = index - " << base << "::getFieldCount()) < 0) {\n"; + out << " " << base << "::setFieldValue(index, value);\n"; + out << " return;\n"; + out << " }\n"; + out << " index = nextIndex;\n"; + } + out << " switch (index) {\n"; + for (int i = 0; i < str.fields.size(); i++) { + out << " case " << i << ":\n"; + out << " this->" << str.fields.at(i).name << " = value.value<" << str.fields.at(i).type << ">();\n"; + out << " break;\n"; + } + out << " }\n"; + out << "}\n"; + out << "Bitstream& operator<<(Bitstream& out, const " << name << "& obj) {\n"; foreach (const QString& base, str.clazz.bases) { out << " out << static_cast(obj);\n"; } - foreach (const QString& field, str.fields) { - out << " out << obj." << field << ";\n"; + foreach (const Field& field, str.fields) { + out << " out << obj." << field.name << ";\n"; } out << " return out;\n"; out << "}\n"; @@ -104,8 +142,8 @@ void generateOutput (QTextStream& out, const QList& streamables) { foreach (const QString& base, str.clazz.bases) { out << " in >> static_cast<" << base << "&>(obj);\n"; } - foreach (const QString& field, str.fields) { - out << " in >> obj." << field << ";\n"; + foreach (const Field& field, str.fields) { + out << " in >> obj." << field.name << ";\n"; } out << " return in;\n"; out << "}\n"; @@ -124,12 +162,12 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "static_cast(first) == static_cast(second)"; first = false; } - foreach (const QString& field, str.fields) { + foreach (const Field& field, str.fields) { if (!first) { out << " &&\n"; out << " "; } - out << "first." << field << " == second." << field; + out << "first." << field.name << " == second." << field.name; first = false; } } @@ -150,19 +188,17 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "static_cast(first) != static_cast(second)"; first = false; } - foreach (const QString& field, str.fields) { + foreach (const Field& field, str.fields) { if (!first) { out << " ||\n"; out << " "; } - out << "first." << field << " != second." << field; + out << "first." << field.name << " != second." << field.name; first = false; } } out << ";\n"; - out << "}\n"; - - out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n"; + out << "}\n\n"; } } From d8b83fd3081bc99db6f622749e69d4616d03f626 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Mar 2014 15:12:43 -0700 Subject: [PATCH 07/38] More work on version-resilient type streaming. --- libraries/metavoxels/src/Bitstream.cpp | 194 ++++++++++++++++++++++--- libraries/metavoxels/src/Bitstream.h | 106 ++++++++++++-- tools/mtc/src/main.cpp | 38 ++++- 3 files changed, 302 insertions(+), 36 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 6c8d8ec559..40c87b98e8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -82,6 +82,10 @@ int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { return 0; } +const TypeStreamer* Bitstream::getTypeStreamer(int type) { + return getTypeStreamers().value(type); +} + const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) { return getMetaObjects().value(className); } @@ -325,11 +329,9 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } Bitstream& Bitstream::operator>>(QVariant& value) { - const TypeStreamer* streamer; - _typeStreamerStreamer >> streamer; - if (streamer) { - value = streamer->read(*this); - } + TypeReader reader; + _typeStreamerStreamer >> reader; + value = reader.read(*this); return *this; } @@ -406,7 +408,14 @@ Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { } Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { - _typeStreamerStreamer >> streamer; + TypeReader typeReader; + _typeStreamerStreamer >> typeReader; + streamer = typeReader.getStreamer(); + return *this; +} + +Bitstream& Bitstream::operator>>(TypeReader& reader) { + _typeStreamerStreamer >> reader; return *this; } @@ -499,8 +508,8 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { *this >> storedPropertyCount; QVector properties(storedPropertyCount); for (int i = 0; i < storedPropertyCount; i++) { - const TypeStreamer* typeStreamer; - *this >> typeStreamer; + TypeReader typeReader; + *this >> typeReader; QMetaProperty property = QMetaProperty(); if (_metadataType == FULL_METADATA) { QByteArray propertyName; @@ -509,7 +518,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { property = metaObject->property(metaObject->indexOfProperty(propertyName)); } } - properties[i] = PropertyReader(typeStreamer, property); + properties[i] = PropertyReader(typeReader, 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) { @@ -525,7 +534,8 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { if (!typeStreamer) { continue; } - if (propertyIndex >= properties.size() || properties.at(propertyIndex).getStreamer() != typeStreamer) { + if (propertyIndex >= properties.size() || + !properties.at(propertyIndex).getReader().matchesExactly(typeStreamer)) { objectReader = ObjectReader(className, metaObject, properties); return *this; } @@ -551,16 +561,106 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { const char* typeName = QMetaType::typeName(streamer->getType()); - return *this << QByteArray::fromRawData(typeName, strlen(typeName)); + *this << QByteArray::fromRawData(typeName, strlen(typeName)); + if (_metadataType == NO_METADATA) { + return *this; + } + 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); + } + return *this; } -Bitstream& Bitstream::operator>(const TypeStreamer*& streamer) { +Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray typeName; *this >> typeName; - streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + const TypeStreamer* streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); if (!streamer) { qWarning() << "Unknown type name: " << typeName << "\n"; } + if (_metadataType == NO_METADATA) { + reader = TypeReader(typeName, streamer); + return *this; + } + int fieldCount; + *this >> fieldCount; + QVector fields(fieldCount); + for (int i = 0; i < fieldCount; i++) { + TypeReader typeReader; + *this >> typeReader; + int index = -1; + if (_metadataType == FULL_METADATA) { + QByteArray fieldName; + *this >> fieldName; + if (streamer) { + index = streamer->getFieldIndex(fieldName); + } + } + fields[i] = FieldReader(typeReader, 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); + if (streamer) { + const QVector& localFields = streamer->getMetaFields(); + if (fieldCount != localFields.size()) { + reader = TypeReader(typeName, streamer, false, fields); + return *this; + } + 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())) { + reader = TypeReader(typeName, streamer, false, fields); + return *this; + } + 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 (streamer && localHashResult == remoteHashResult) { + // since everything is the same, we can use the default streamer + reader = TypeReader(typeName, streamer); + return *this; + } + } else if (streamer) { + // 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(); + if (fieldCount != localFields.size()) { + reader = TypeReader(typeName, streamer, false, 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, false, fields); + return *this; + } + } + reader = TypeReader(typeName, streamer); + return *this; + } + reader = TypeReader(typeName, streamer, false, fields); return *this; } @@ -650,12 +750,51 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj } const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType()); if (typeStreamer) { - propertyReaders.append(PropertyReader(typeStreamer, property)); + propertyReaders.append(PropertyReader(TypeReader(QByteArray(), typeStreamer), property)); } } return propertyReaders; } +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, + bool exactMatch, const QVector& fields) : + _typeName(typeName), + _streamer(streamer), + _exactMatch(exactMatch), + _fields(fields) { +} + +QVariant TypeReader::read(Bitstream& in) const { + if (_exactMatch) { + return _streamer->read(in); + } + QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); + foreach (const FieldReader& field, _fields) { + field.read(in, _streamer, object); + } + return object; +} + +bool TypeReader::matchesExactly(const TypeStreamer* streamer) const { + return _exactMatch && _streamer == streamer; +} + +uint qHash(const TypeReader& typeReader, uint seed) { + return qHash(typeReader.getTypeName(), seed); +} + +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(_index, object, value); + } +} + ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const QVector& properties) : _className(className), @@ -677,14 +816,35 @@ uint qHash(const ObjectReader& objectReader, uint seed) { return qHash(objectReader.getClassName(), seed); } -PropertyReader::PropertyReader(const TypeStreamer* streamer, const QMetaProperty& property) : - _streamer(streamer), +PropertyReader::PropertyReader(const TypeReader& reader, const QMetaProperty& property) : + _reader(reader), _property(property) { } void PropertyReader::read(Bitstream& in, QObject* object) const { - QVariant value = _streamer->read(in); + QVariant value = _reader.read(in); if (_property.isValid() && object) { _property.write(object, value); } } + +MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : + _name(name), + _streamer(streamer) { +} + +TypeStreamer::~TypeStreamer() { +} + +const QVector& TypeStreamer::getMetaFields() const { + static QVector emptyMetaFields; + return emptyMetaFields; +} + +int TypeStreamer::getFieldIndex(const QByteArray& name) const { + return -1; +} + +void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) const { + // nothing by default +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index f52db029b6..78bc125841 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -30,9 +31,11 @@ class QUrl; class Attribute; class AttributeValue; class Bitstream; +class FieldReader; class ObjectReader; class OwnedAttributeValue; class PropertyReader; +class TypeReader; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -191,7 +194,7 @@ public: class ReadMappings { public: QHash metaObjectValues; - QHash typeStreamerValues; + QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; QHash sharedObjectValues; @@ -205,6 +208,9 @@ public: /// \return zero; the function only returns a value so that it can be used in static initialization static int registerTypeStreamer(int type, TypeStreamer* streamer); + /// Returns the streamer registered for the supplied type, if any. + static const TypeStreamer* getTypeStreamer(int type); + /// Returns the meta-object registered under the supplied class name, if any. static const QMetaObject* getMetaObject(const QByteArray& className); @@ -307,6 +313,7 @@ public: Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); + Bitstream& operator>>(TypeReader& reader); Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); @@ -321,7 +328,7 @@ public: Bitstream& operator>(ObjectReader& objectReader); Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(const TypeStreamer*& streamer); + Bitstream& operator>(TypeReader& reader); Bitstream& operator<(const AttributePointer& attribute); Bitstream& operator>(AttributePointer& attribute); @@ -349,7 +356,7 @@ private: MetadataType _metadataType; RepeatedValueStreamer _metaObjectStreamer; - RepeatedValueStreamer _typeStreamerStreamer; + RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; @@ -428,6 +435,50 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +/// Contains the information required to read a type from the stream. +class TypeReader { +public: + + TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, + bool exactMatch = true, const QVector& fields = QVector()); + + const QByteArray& getTypeName() const { return _typeName; } + const TypeStreamer* getStreamer() const { return _streamer; } + + QVariant read(Bitstream& in) 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; + QVector _fields; +}; + +uint qHash(const TypeReader& typeReader, uint seed = 0); + +/// 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; + +private: + + TypeReader _reader; + int _index; +}; + /// Contains the information required to read an object from the stream. class ObjectReader { public: @@ -438,8 +489,6 @@ public: const QByteArray& getClassName() const { return _className; } const QMetaObject* getMetaObject() const { return _metaObject; } - bool isNull() const { return _className.isEmpty(); } - QObject* read(Bitstream& in, QObject* object = NULL) const; bool operator==(const ObjectReader& other) const { return _className == other._className; } @@ -458,18 +507,33 @@ uint qHash(const ObjectReader& objectReader, uint seed = 0); class PropertyReader { public: - PropertyReader(const TypeStreamer* streamer = NULL, const QMetaProperty& property = QMetaProperty()); + PropertyReader(const TypeReader& reader = TypeReader(), const QMetaProperty& property = QMetaProperty()); - const TypeStreamer* getStreamer() const { return _streamer; } + const TypeReader& getReader() const { return _reader; } void read(Bitstream& in, QObject* object) const; private: - const TypeStreamer* _streamer; + TypeReader _reader; QMetaProperty _property; }; +/// Describes a metatype field. +class MetaField { +public: + + MetaField(const QByteArray& name = QByteArray(), const TypeStreamer* streamer = NULL); + + const QByteArray& getName() const { return _name; } + const TypeStreamer* getStreamer() const { return _streamer; } + +private: + + QByteArray _name; + const TypeStreamer* _streamer; +}; + Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. @@ -479,12 +543,18 @@ Q_DECLARE_METATYPE(const QMetaObject*) class TypeStreamer { public: + virtual ~TypeStreamer(); + void setType(int type) { _type = type; } int getType() const { return _type; } virtual void write(Bitstream& out, const QVariant& value) const = 0; virtual QVariant read(Bitstream& in) const = 0; + virtual const QVector& getMetaFields() const; + virtual int getFieldIndex(const QByteArray& name) const; + virtual void setField(int index, QVariant& object, const QVariant& value) const; + private: int _type; @@ -498,6 +568,16 @@ public: virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } }; +/// A streamer that works with Bitstream's operators. +template class CompoundTypeStreamer : public SimpleTypeStreamer { +public: + + virtual const QVector& getMetaFields() const { return T::getMetaFields(); } + virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } + virtual void setField(int index, QVariant& object, const QVariant& value) const { + static_cast(object.data())->setField(index, value); } +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); @@ -532,16 +612,18 @@ public: /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer()); + Bitstream::registerTypeStreamer(type, new CompoundTypeStreamer()); return type; } /// Flags a class as streamable (use as you would Q_OBJECT). #define STREAMABLE public: \ static const int Type; \ - static int getFieldCount(); \ - void setFieldValue(int index, const QVariant& value); \ - private: + static const QVector& getMetaFields(); \ + static int getFieldIndex(const QByteArray& name); \ + void setField(int index, const QVariant& value); \ + private: \ + static QHash createFieldIndices(); /// Flags a field or base class as streaming. #define STREAM diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 598b7b10d0..562dd6e02e 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -100,21 +100,45 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n"; - out << "int " << name << "::getFieldCount() {\n"; - out << " return " << str.fields.size(); - foreach(const QString& base, str.clazz.bases) { - out << " + " << base << "::getFieldCount()"; + out << "const QVector& " << name << "::getMetaFields() {\n"; + out << " static QVector metaFields = QVector()"; + foreach (const QString& base, str.clazz.bases) { + out << " << " << base << "::getMetaFields()"; + } + foreach (const Field& field, str.fields) { + out << "\n << MetaField(\"" << field.name << "\", Bitstream::getTypeStreamer(qMetaTypeId<" << + field.type << ">()))"; } out << ";\n"; + out << " return metaFields;\n"; out << "}\n"; - out << "void " << name << "::setFieldValue(int index, const QVariant& value) {\n"; + out << "int " << name << "::getFieldIndex(const QByteArray& name) {\n"; + out << " static QHash fieldIndices = createFieldIndices();\n"; + out << " return fieldIndices.value(name) - 1;\n"; + out << "}\n"; + + out << "QHash " << name << "::createFieldIndices() {\n"; + out << " QHash indices;\n"; + out << " int index = 0;\n"; + foreach (const QString& base, str.clazz.bases) { + out << " foreach (const MetaField& field, " << base << "::getMetaFields()) {\n"; + out << " indices.insert(field.getName(), index++);\n"; + out << " }\n"; + } + out << " foreach (const MetaField& field, getMetaFields()) {\n"; + out << " indices.insert(field.getName(), index++);\n"; + out << " }\n"; + out << " return indices;\n"; + out << "}\n"; + + out << "void " << name << "::setField(int index, const QVariant& value) {\n"; if (!str.clazz.bases.isEmpty()) { out << " int nextIndex;\n"; } foreach (const QString& base, str.clazz.bases) { - out << " if ((nextIndex = index - " << base << "::getFieldCount()) < 0) {\n"; - out << " " << base << "::setFieldValue(index, value);\n"; + out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n"; + out << " " << base << "::setField(index, value);\n"; out << " return;\n"; out << " }\n"; out << " index = nextIndex;\n"; From 6d14ad6ab5b522897787acf949a762fb10d28c4e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Mar 2014 15:13:14 -0700 Subject: [PATCH 08/38] Missed a spot. --- tools/mtc/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 562dd6e02e..7a546ab529 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -120,7 +120,7 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "QHash " << name << "::createFieldIndices() {\n"; out << " QHash indices;\n"; - out << " int index = 0;\n"; + out << " int index = 1;\n"; foreach (const QString& base, str.clazz.bases) { out << " foreach (const MetaField& field, " << base << "::getMetaFields()) {\n"; out << " indices.insert(field.getName(), index++);\n"; From eb95b01aa1a4ee17990e5d39163d06ef035b0fe7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Mar 2014 16:10:30 -0700 Subject: [PATCH 09/38] Fixes to, test for metadata streaming. --- libraries/metavoxels/src/Bitstream.cpp | 28 ++++++--- libraries/metavoxels/src/Bitstream.h | 9 +++ tests/metavoxels/src/MetavoxelTests.cpp | 76 +++++++++++++++++-------- tests/metavoxels/src/MetavoxelTests.h | 15 ++++- 4 files changed, 97 insertions(+), 31 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 40c87b98e8..c2688e7607 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -107,6 +107,14 @@ Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, QObject _sharedObjectStreamer(*this) { } +void Bitstream::addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject) { + _metaObjectSubstitutions.insert(className, metaObject); +} + +void Bitstream::addTypeSubstitution(const QByteArray& typeName, int type) { + _typeStreamerSubstitutions.insert(typeName, getTypeStreamers().value(type)); +} + const int LAST_BIT_POSITION = BITS_IN_BYTE - 1; Bitstream& Bitstream::write(const void* data, int bits, int offset) { @@ -496,7 +504,10 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { objectReader = ObjectReader(); return *this; } - const QMetaObject* metaObject = getMetaObjects().value(className); + const QMetaObject* metaObject = _metaObjectSubstitutions.value(className); + if (!metaObject) { + metaObject = getMetaObjects().value(className); + } if (!metaObject) { qWarning() << "Unknown class name: " << className << "\n"; } @@ -523,6 +534,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { // 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) { int propertyIndex = 0; for (int i = 0; i < metaObject->propertyCount(); i++) { @@ -536,21 +548,20 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { } if (propertyIndex >= properties.size() || !properties.at(propertyIndex).getReader().matchesExactly(typeStreamer)) { - objectReader = ObjectReader(className, metaObject, properties); - return *this; + matches = false; + break; } hash.addData(property.name(), strlen(property.name()) + 1); propertyIndex++; } if (propertyIndex != properties.size()) { - objectReader = ObjectReader(className, metaObject, properties); - return *this; + matches = false; } } QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (metaObject && localHashResult == remoteHashResult) { + if (metaObject && matches && localHashResult == remoteHashResult) { objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject)); return *this; } @@ -589,7 +600,10 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray typeName; *this >> typeName; - const TypeStreamer* streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName); + if (!streamer) { + streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + } if (!streamer) { qWarning() << "Unknown type name: " << typeName << "\n"; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 78bc125841..b51bee3a25 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -222,6 +222,12 @@ public: /// 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); + /// Substitutes the supplied metaobject for the given class name's default mapping. + void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject); + + /// Substitutes the supplied type for the given type name's default mapping. + void addTypeSubstitution(const QByteArray& typeName, int type); + /// Writes a set of bits to the underlying stream. /// \param bits the number of bits to write /// \param offset the offset of the first bit @@ -363,6 +369,9 @@ private: WeakSharedObjectHash _weakSharedObjectHash; + QHash _metaObjectSubstitutions; + QHash _typeStreamerSubstitutions; + static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 9de48364b6..a178215fbc 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -34,23 +34,65 @@ static int streamedBytesReceived = 0; static int sharedObjectsCreated = 0; static int sharedObjectsDestroyed = 0; +static QByteArray createRandomBytes(int minimumSize, int maximumSize) { + QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); + for (int i = 0; i < bytes.size(); i++) { + bytes[i] = rand(); + } + return bytes; +} + +static QByteArray createRandomBytes() { + const int MIN_BYTES = 4; + const int MAX_BYTES = 16; + return createRandomBytes(MIN_BYTES, MAX_BYTES); +} + static bool testSerialization(Bitstream::MetadataType metadataType) { QByteArray array; QDataStream outStream(&array, QIODevice::WriteOnly); Bitstream out(outStream, metadataType); - SharedObjectPointer testObjectWritten = new TestSharedObjectA(randFloat()); - out << testObjectWritten; + SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat()); + out << testObjectWrittenA; + SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes()); + out << testObjectWrittenB; + QByteArray endWritten = "end"; + out << endWritten; out.flush(); QDataStream inStream(array); Bitstream in(inStream, metadataType); - SharedObjectPointer testObjectRead; - in >> testObjectRead; + in.addMetaObjectSubstitution("TestSharedObjectA", &TestSharedObjectB::staticMetaObject); + in.addMetaObjectSubstitution("TestSharedObjectB", &TestSharedObjectA::staticMetaObject); + SharedObjectPointer testObjectReadA, testObjectReadB; + in >> testObjectReadA >> testObjectReadB; - if (!testObjectWritten->equals(testObjectRead)) { - QDebug debug = qDebug() << "Read/write mismatch"; - testObjectWritten->dump(debug); - testObjectRead->dump(debug); + if (!testObjectReadA || testObjectReadA->metaObject() != &TestSharedObjectB::staticMetaObject) { + qDebug() << "Wrong class for A" << testObjectReadA << metadataType; + return true; + } + if (metadataType == Bitstream::FULL_METADATA && static_cast(testObjectWrittenA.data())->getFoo() != + static_cast(testObjectReadA.data())->getFoo()) { + QDebug debug = qDebug() << "Failed to transfer shared field from A to B"; + testObjectWrittenA->dump(debug); + testObjectReadA->dump(debug); + } + + if (!testObjectReadB || testObjectReadB->metaObject() != &TestSharedObjectA::staticMetaObject) { + qDebug() << "Wrong class for B" << testObjectReadB << metadataType; + return true; + } + if (metadataType == Bitstream::FULL_METADATA && static_cast(testObjectWrittenB.data())->getFoo() != + static_cast(testObjectReadB.data())->getFoo()) { + QDebug debug = qDebug() << "Failed to transfer shared field from B to A"; + testObjectWrittenB->dump(debug); + testObjectReadB->dump(debug); + } + + QByteArray endRead; + in >> endRead; + if (endWritten != endRead) { + qDebug() << "End tag mismatch." << endRead; return true; } @@ -100,20 +142,6 @@ bool MetavoxelTests::run() { return false; } -static QByteArray createRandomBytes(int minimumSize, int maximumSize) { - QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); - for (int i = 0; i < bytes.size(); i++) { - bytes[i] = rand(); - } - return bytes; -} - -static QByteArray createRandomBytes() { - const int MIN_BYTES = 4; - const int MAX_BYTES = 16; - return createRandomBytes(MIN_BYTES, MAX_BYTES); -} - static SharedObjectPointer createRandomSharedObject() { switch (randIntInRange(0, 2)) { case 0: return new TestSharedObjectA(randFloat()); @@ -354,7 +382,9 @@ void TestSharedObjectA::setFoo(float foo) { } } -TestSharedObjectB::TestSharedObjectB() { +TestSharedObjectB::TestSharedObjectB(float foo, const QByteArray& bar) : + _foo(foo), + _bar(bar) { sharedObjectsCreated++; } diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 3b69c28f79..22a680acbb 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -89,11 +89,24 @@ private: /// Another simple shared object. class TestSharedObjectB : public SharedObject { Q_OBJECT + Q_PROPERTY(float foo READ getFoo WRITE setFoo) + Q_PROPERTY(QByteArray bar READ getBar WRITE setBar) public: - Q_INVOKABLE TestSharedObjectB(); + Q_INVOKABLE TestSharedObjectB(float foo = 0.0f, const QByteArray& bar = QByteArray()); virtual ~TestSharedObjectB(); + + void setFoo(float foo) { _foo = foo; } + float getFoo() const { return _foo; } + + void setBar(const QByteArray& bar) { _bar = bar; } + const QByteArray& getBar() const { return _bar; } + +private: + + float _foo; + QByteArray _bar; }; /// A simple test message. From d569802fbcf54640a319090074d82b91c8923762 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Mar 2014 17:21:46 -0700 Subject: [PATCH 10/38] More metadata fixes. --- libraries/metavoxels/src/Bitstream.cpp | 29 +++++++++--------- libraries/metavoxels/src/Bitstream.h | 2 +- tests/metavoxels/src/MetavoxelTests.cpp | 39 ++++++++++++++++++++----- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index c2688e7607..68f1e19dc4 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -31,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(QString) REGISTER_SIMPLE_TYPE_STREAMER(QUrl) REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) REGISTER_SIMPLE_TYPE_STREAMER(QVariantHash) +REGISTER_SIMPLE_TYPE_STREAMER(SharedObjectPointer) // some types don't quite work with our macro static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); @@ -630,29 +631,31 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { // 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 (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, false, fields); - return *this; - } - 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())) { - reader = TypeReader(typeName, streamer, false, fields); + matches = false; + + } else { + if (fieldCount == 0) { + reader = TypeReader(typeName, streamer); return *this; } - hash.addData(localField.getName().constData(), localField.getName().size() + 1); + for (int i = 0; i < fieldCount; i++) { + const MetaField& localField = localFields.at(i); + if (!fields.at(i).getReader().matchesExactly(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 (streamer && localHashResult == remoteHashResult) { + if (streamer && matches && localHashResult == remoteHashResult) { // since everything is the same, we can use the default streamer reader = TypeReader(typeName, streamer); return *this; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index b51bee3a25..2ed265b6ed 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -589,7 +589,7 @@ public: /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ - Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); + Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); /// Declares the metatype and the streaming operators. The last lines /// ensure that the generated file will be included in the link phase. diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index a178215fbc..a1b79319b5 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -48,6 +48,15 @@ static QByteArray createRandomBytes() { return createRandomBytes(MIN_BYTES, MAX_BYTES); } +static TestMessageC createRandomMessageC() { + TestMessageC message; + message.foo = randomBoolean(); + message.bar = rand(); + message.baz = randFloat(); + message.bong.foo = createRandomBytes(); + return message; +} + static bool testSerialization(Bitstream::MetadataType metadataType) { QByteArray array; QDataStream outStream(&array, QIODevice::WriteOnly); @@ -56,6 +65,8 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { out << testObjectWrittenA; SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes()); out << testObjectWrittenB; + TestMessageC messageWritten = createRandomMessageC(); + out << QVariant::fromValue(messageWritten); QByteArray endWritten = "end"; out << endWritten; out.flush(); @@ -64,8 +75,9 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { Bitstream in(inStream, metadataType); in.addMetaObjectSubstitution("TestSharedObjectA", &TestSharedObjectB::staticMetaObject); in.addMetaObjectSubstitution("TestSharedObjectB", &TestSharedObjectA::staticMetaObject); - SharedObjectPointer testObjectReadA, testObjectReadB; - in >> testObjectReadA >> testObjectReadB; + in.addTypeSubstitution("TestMessageC", TestMessageA::Type); + SharedObjectPointer testObjectReadA; + in >> testObjectReadA; if (!testObjectReadA || testObjectReadA->metaObject() != &TestSharedObjectB::staticMetaObject) { qDebug() << "Wrong class for A" << testObjectReadA << metadataType; @@ -76,8 +88,11 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { QDebug debug = qDebug() << "Failed to transfer shared field from A to B"; testObjectWrittenA->dump(debug); testObjectReadA->dump(debug); + return true; } + SharedObjectPointer testObjectReadB; + in >> testObjectReadB; if (!testObjectReadB || testObjectReadB->metaObject() != &TestSharedObjectA::staticMetaObject) { qDebug() << "Wrong class for B" << testObjectReadB << metadataType; return true; @@ -87,6 +102,19 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { QDebug debug = qDebug() << "Failed to transfer shared field from B to A"; testObjectWrittenB->dump(debug); testObjectReadB->dump(debug); + return true; + } + + QVariant messageRead; + in >> messageRead; + if (!messageRead.isValid() || messageRead.userType() != TestMessageA::Type) { + qDebug() << "Wrong type for message" << messageRead; + return true; + } + if (metadataType == Bitstream::FULL_METADATA && messageWritten.foo != messageRead.value().foo) { + QDebug debug = qDebug() << "Failed to transfer shared field between messages" << + messageWritten.foo << messageRead.value().foo; + return true; } QByteArray endRead; @@ -192,12 +220,7 @@ static QVariant createRandomMessage() { } case 2: default: { - TestMessageC message; - message.foo = randomBoolean(); - message.bar = rand(); - message.baz = randFloat(); - message.bong.foo = createRandomBytes(); - return QVariant::fromValue(message); + return QVariant::fromValue(createRandomMessageC()); } } } From a38239b3d8fe0714a0b62c2ad3330d90893db1cd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Mar 2014 10:11:25 -0700 Subject: [PATCH 11/38] Working on collection streaming. --- libraries/metavoxels/src/Bitstream.cpp | 16 +++++++++ libraries/metavoxels/src/Bitstream.h | 46 ++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 68f1e19dc4..f6e988a5f2 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -865,3 +865,19 @@ int TypeStreamer::getFieldIndex(const QByteArray& name) const { void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) const { // nothing by default } + +const TypeStreamer* TypeStreamer::getKeyStreamer() const { + return NULL; +} + +const TypeStreamer* TypeStreamer::getValueStreamer() const { + return NULL; +} + +void TypeStreamer::append(QVariant& object, const QVariant& element) const { + // nothing by default +} + +void TypeStreamer::insert(QVariant& object, const QVariant& key, const QVariant& value) const { + // nothing by default +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 2ed265b6ed..5d5c75857d 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -552,6 +552,8 @@ Q_DECLARE_METATYPE(const QMetaObject*) class TypeStreamer { public: + enum Category { SIMPLE_CATEGORY, STREAMABLE_CATEGORY }; + virtual ~TypeStreamer(); void setType(int type) { _type = type; } @@ -564,6 +566,12 @@ public: virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(int index, QVariant& object, const QVariant& value) const; + virtual const TypeStreamer* getKeyStreamer() const; + virtual const TypeStreamer* getValueStreamer() const; + + virtual void append(QVariant& object, const QVariant& element) const; + virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const; + private: int _type; @@ -577,8 +585,8 @@ public: virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } }; -/// A streamer that works with Bitstream's operators. -template class CompoundTypeStreamer : public SimpleTypeStreamer { +/// A streamer for types compiled by mtc. +template class StreamableTypeStreamer : public SimpleTypeStreamer { public: virtual const QVector& getMetaFields() const { return T::getMetaFields(); } @@ -587,10 +595,42 @@ public: static_cast(object.data())->setField(index, value); } }; +/// Base template for collection streamers. +template class CollectionTypeStreamer : public SimpleTypeStreamer { +}; + +/// A streamer for list types. +template class CollectionTypeStreamer > : public SimpleTypeStreamer > { +public: + + virtual void append(QVariant& object, const QVariant& element) const { + static_cast*>(object.data())->append(element.value()); } +}; + +/// A streamer for set types. +template class CollectionTypeStreamer > : public SimpleTypeStreamer > { +public: + + virtual void append(QVariant& object, const QVariant& element) const { + static_cast*>(object.data())->insert(element.value()); } +}; + +/// A streamer for hash types. +template class CollectionTypeStreamer > : public SimpleTypeStreamer > { +public: + + virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const { + static_cast*>(object.data())->insert(key.value(), value.value()); } +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); +/// Macro for registering collection type streamers. +#define REGISTER_COLLECTION_TYPE_STREAMER(x) static int x##Streamer = \ + Bitstream::registerTypeStreamer(qMetaTypeId(), new CollectionTypeStreamer()); + /// Declares the metatype and the streaming operators. The last lines /// ensure that the generated file will be included in the link phase. #ifdef _WIN32 @@ -621,7 +661,7 @@ public: /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new CompoundTypeStreamer()); + Bitstream::registerTypeStreamer(type, new StreamableTypeStreamer()); return type; } From a4cc15a2cdafe56ab8d4302dd5a3d700db142062 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Mar 2014 11:46:12 -0700 Subject: [PATCH 12/38] Collection streaming (untested). --- libraries/metavoxels/src/Bitstream.cpp | 93 +++++++++++++++++++++++--- libraries/metavoxels/src/Bitstream.h | 33 ++++++--- 2 files changed, 109 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index f6e988a5f2..0708ede5b6 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -577,6 +577,22 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { if (_metadataType == NO_METADATA) { return *this; } + TypeStreamer::Category category = streamer->getCategory(); + *this << (int)category; + switch (category) { + case TypeStreamer::SIMPLE_CATEGORY: + return *this; + + case TypeStreamer::LIST_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()) { @@ -612,6 +628,40 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { reader = TypeReader(typeName, streamer); return *this; } + int category; + *this >> category; + switch (category) { + case TypeStreamer::SIMPLE_CATEGORY: + reader = TypeReader(typeName, streamer); + return *this; + + case TypeStreamer::LIST_CATEGORY: { + TypeReader valueReader; + *this >> valueReader; + if (streamer && streamer->getCategory() == TypeStreamer::LIST_CATEGORY && + valueReader.matchesExactly(streamer->getValueStreamer())) { + reader = TypeReader(typeName, streamer); + } else { + reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), + TypeReaderPointer(new TypeReader(valueReader))); + } + return *this; + } + case TypeStreamer::MAP_CATEGORY: { + TypeReader keyReader, valueReader; + *this >> keyReader >> valueReader; + if (streamer && streamer->getCategory() == TypeStreamer::MAP_CATEGORY && + keyReader.matchesExactly(streamer->getKeyStreamer()) && + valueReader.matchesExactly(streamer->getValueStreamer())) { + reader = TypeReader(typeName, streamer); + } else { + reader = TypeReader(typeName, streamer, false, TypeReaderPointer(new TypeReader(keyReader)), + TypeReaderPointer(new TypeReader(valueReader))); + } + return *this; + } + } + // streamable type int fieldCount; *this >> fieldCount; QVector fields(fieldCount); @@ -664,20 +714,20 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { // 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(); if (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, false, fields); + reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), 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, false, fields); + reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } } reader = TypeReader(typeName, streamer); return *this; } - reader = TypeReader(typeName, streamer, false, fields); + reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } @@ -773,11 +823,13 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj return propertyReaders; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - bool exactMatch, const QVector& fields) : +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, + const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector& fields) : _typeName(typeName), _streamer(streamer), _exactMatch(exactMatch), + _keyReader(keyReader), + _valueReader(valueReader), _fields(fields) { } @@ -786,8 +838,29 @@ QVariant TypeReader::read(Bitstream& in) const { return _streamer->read(in); } QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); - foreach (const FieldReader& field, _fields) { - field.read(in, _streamer, object); + if (_valueReader) { + int size; + in >> size; + if (_keyReader) { + for (int i = 0; i < size; i++) { + QVariant key = _keyReader->read(in); + QVariant value = _valueReader->read(in); + if (_streamer) { + _streamer->insert(object, key, value); + } + } + } else { + for (int i = 0; i < size; i++) { + QVariant value = _valueReader->read(in); + if (_streamer) { + _streamer->insert(object, value); + } + } + } + } else { + foreach (const FieldReader& field, _fields) { + field.read(in, _streamer, object); + } } return object; } @@ -866,6 +939,10 @@ void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) // nothing by default } +TypeStreamer::Category TypeStreamer::getCategory() const { + return SIMPLE_CATEGORY; +} + const TypeStreamer* TypeStreamer::getKeyStreamer() const { return NULL; } @@ -874,7 +951,7 @@ const TypeStreamer* TypeStreamer::getValueStreamer() const { return NULL; } -void TypeStreamer::append(QVariant& object, const QVariant& element) const { +void TypeStreamer::insert(QVariant& object, const QVariant& element) const { // nothing by default } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 5d5c75857d..769ae4a250 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -444,12 +444,15 @@ 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: - TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, - bool exactMatch = true, const QVector& fields = QVector()); + TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true, + const TypeReaderPointer& keyReader = TypeReaderPointer(), const TypeReaderPointer& valueReader = TypeReaderPointer(), + const QVector& fields = QVector()); const QByteArray& getTypeName() const { return _typeName; } const TypeStreamer* getStreamer() const { return _streamer; } @@ -466,6 +469,8 @@ private: QByteArray _typeName; const TypeStreamer* _streamer; bool _exactMatch; + TypeReaderPointer _keyReader; + TypeReaderPointer _valueReader; QVector _fields; }; @@ -552,8 +557,6 @@ Q_DECLARE_METATYPE(const QMetaObject*) class TypeStreamer { public: - enum Category { SIMPLE_CATEGORY, STREAMABLE_CATEGORY }; - virtual ~TypeStreamer(); void setType(int type) { _type = type; } @@ -565,11 +568,15 @@ public: virtual const QVector& getMetaFields() const; virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(int index, QVariant& object, const QVariant& value) const; + + enum Category { SIMPLE_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, MAP_CATEGORY }; + + virtual Category getCategory() const; virtual const TypeStreamer* getKeyStreamer() const; virtual const TypeStreamer* getValueStreamer() const; - virtual void append(QVariant& object, const QVariant& element) const; + virtual void insert(QVariant& object, const QVariant& value) const; virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const; private: @@ -589,6 +596,7 @@ public: template class StreamableTypeStreamer : public SimpleTypeStreamer { public: + 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(int index, QVariant& object, const QVariant& value) const { @@ -603,22 +611,29 @@ template class CollectionTypeStreamer : public SimpleTypeStreamer { template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual void append(QVariant& object, const QVariant& element) const { - static_cast*>(object.data())->append(element.value()); } + 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()); } }; /// A streamer for set types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual void append(QVariant& object, const QVariant& element) const { - static_cast*>(object.data())->insert(element.value()); } + 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())->insert(value.value()); } }; /// A streamer for hash types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + 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 { static_cast*>(object.data())->insert(key.value(), value.value()); } }; From 24b2da1c0de3c8214a0340de947e47833ad18afb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Mar 2014 13:14:21 -0700 Subject: [PATCH 13/38] Working on delta streaming. --- libraries/metavoxels/src/Bitstream.cpp | 19 ++++++++++++ libraries/metavoxels/src/Bitstream.h | 37 ++++++++++++++++++++++- libraries/metavoxels/src/SharedObject.cpp | 10 +++++- libraries/metavoxels/src/SharedObject.h | 5 ++- tools/mtc/src/main.cpp | 20 ++++++++++++ 5 files changed, 88 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 0708ede5b6..6bad482ca1 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -865,6 +865,21 @@ QVariant TypeReader::read(Bitstream& in) const { return object; } +void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + if (_exactMatch) { + _streamer->readDelta(in, object, reference); + return; + } + if (_valueReader) { + // TODO: collection deltas + + } else { + foreach (const FieldReader& field, _fields) { + field.readDelta(in, _streamer, object, reference); + } + } +} + bool TypeReader::matchesExactly(const TypeStreamer* streamer) const { return _exactMatch && _streamer == streamer; } @@ -885,6 +900,10 @@ void FieldReader::read(Bitstream& in, const TypeStreamer* streamer, QVariant& ob } } +void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const { + // TODO: field delta +} + ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const QVector& properties) : _className(className), diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 769ae4a250..cd885d9f81 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -268,6 +268,9 @@ public: /// Removes a shared object from the read mappings. void clearSharedObject(int id); + template void writeDelta(const T& value, const T& reference); + template void readDelta(T& value, const T& reference); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -378,6 +381,23 @@ private: static QVector getPropertyReaders(const QMetaObject* metaObject); }; +template inline void Bitstream::writeDelta(const T& value, const T& reference) { + if (value == reference) { + *this << false; + } else { + *this << true; + *this << value; + } +} + +template inline void Bitstream::readDelta(T& value, const T& reference) { + bool changed; + *this >> changed; + if (changed) { + *this >> value; + } +} + template inline Bitstream& Bitstream::operator<<(const QList& list) { *this << list.size(); foreach (const T& entry, list) { @@ -458,6 +478,7 @@ public: const TypeStreamer* getStreamer() const { return _streamer; } QVariant read(Bitstream& in) const; + void readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; bool matchesExactly(const TypeStreamer* streamer) const; @@ -486,7 +507,8 @@ public: 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; @@ -565,6 +587,9 @@ public: virtual void write(Bitstream& out, const QVariant& value) const = 0; virtual QVariant read(Bitstream& in) const = 0; + 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 const QVector& getMetaFields() const; virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(int index, QVariant& object, const QVariant& value) const; @@ -590,6 +615,10 @@ public: virtual void write(Bitstream& out, const QVariant& value) const { out << value.value(); } virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } + virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + out.writeDelta(value.value(), reference.value()); } + virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + in.readDelta(*static_cast(value.data()), reference.value()); } }; /// A streamer for types compiled by mtc. @@ -652,6 +681,8 @@ public: #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ + template<> void Bitstream::writeDelta(const X& value, const X& reference); \ + template<> void Bitstream::readDelta(X& value, const X& reference); \ bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ static const int* _TypePtr##X = &X::Type; @@ -659,6 +690,8 @@ public: #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ + template<> void Bitstream::writeDelta(const X& value, const X& reference); \ + template<> void Bitstream::readDelta(X& value, const X& reference); \ bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ __attribute__((unused)) static const int* _TypePtr##X = &X::Type; @@ -667,6 +700,8 @@ public: #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ + template<> void Bitstream::writeDelta(const X& value, const X& reference); \ + template<> void Bitstream::readDelta(X& value, const X& reference); \ bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ static const int* _TypePtr##X = &X::Type; \ diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index e95958ac76..36257a740f 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -37,7 +37,7 @@ void SharedObject::decrementReferenceCount() { } } -SharedObject* SharedObject::clone() const { +SharedObject* SharedObject::clone(bool withID) const { // default behavior is to make a copy using the no-arg constructor and copy the stored properties const QMetaObject* metaObject = this->metaObject(); SharedObject* newObject = static_cast(metaObject->newInstance()); @@ -50,6 +50,9 @@ SharedObject* SharedObject::clone() const { foreach (const QByteArray& propertyName, dynamicPropertyNames()) { newObject->setProperty(propertyName, property(propertyName)); } + if (withID) { + newObject->setID(_id); + } return newObject; } @@ -91,6 +94,11 @@ void SharedObject::dump(QDebug debug) const { } } +void SharedObject::setID(int id) { + _weakHash.remove(_id); + _weakHash.insert(_id = id, this); +} + int SharedObject::_lastID = 0; WeakSharedObjectHash SharedObject::_weakHash; diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 66e6474972..435127fffd 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -47,7 +47,8 @@ public: void decrementReferenceCount(); /// Creates a new clone of this object. - virtual SharedObject* clone() const; + /// \param withID if true, give the clone the same ID as this object + virtual SharedObject* clone(bool withID = false) const; /// Tests this object for equality with another. virtual bool equals(const SharedObject* other) const; @@ -57,6 +58,8 @@ public: private: + void setID(int id); + int _id; int _remoteID; int _referenceCount; diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 7a546ab529..e6f1827870 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -172,6 +172,26 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << " return in;\n"; out << "}\n"; + out << "template<> void Bitstream::writeDelta(const " << name << "& value, const " << name << "& reference) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " writeDelta(static_cast(value), static_cast(reference));\n"; + } + foreach (const Field& field, str.fields) { + out << " writeDelta(value." << field.name << ", reference." << field.name << ");\n"; + } + out << "}\n"; + + out << "template<> void Bitstream::readDelta(" << name << "& value, const " << name << "& reference) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " readDelta(static_cast<" << base << "&>(value), static_cast(reference));\n"; + } + foreach (const Field& field, str.fields) { + out << " readDelta(value." << field.name << ", reference." << field.name << ");\n"; + } + out << "}\n"; + out << "bool operator==(const " << name << "& first, const " << name << "& second) {\n"; if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) { out << " return true"; From 20e9572f608bbe8d7439b7231129666cdca87a66 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Mar 2014 14:20:05 -0700 Subject: [PATCH 14/38] Working on specialized delta streaming. --- libraries/metavoxels/src/Bitstream.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index cd885d9f81..49b4ddd95f 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -271,6 +271,9 @@ public: template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); + template void writeDelta(const QSet& value, const QSet& reference); + template void readDelta(QSet& value, const QSet& reference); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -395,9 +398,19 @@ template inline void Bitstream::readDelta(T& value, const T& reference) *this >> changed; if (changed) { *this >> value; + } else { + value = reference; } } +template inline void Bitstream::writeDelta(const QSet& value, const QSet& reference) { + +} + +template inline void Bitstream::readDelta(QSet& value, const QSet& reference) { + +} + template inline Bitstream& Bitstream::operator<<(const QList& list) { *this << list.size(); foreach (const T& entry, list) { From 9c2f6ab2e598fff890b0c0ad8b4a21e46be5dc51 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Mar 2014 18:28:43 -0700 Subject: [PATCH 15/38] Working on delta streaming for collections. --- libraries/metavoxels/src/Bitstream.cpp | 164 +++++++++++++++------ libraries/metavoxels/src/Bitstream.h | 192 +++++++++++++++++++++++-- tools/mtc/src/main.cpp | 11 ++ 3 files changed, 312 insertions(+), 55 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 6bad482ca1..070ad550d8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -577,16 +577,17 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { if (_metadataType == NO_METADATA) { return *this; } - TypeStreamer::Category category = streamer->getCategory(); - *this << (int)category; - switch (category) { - case TypeStreamer::SIMPLE_CATEGORY: + TypeReader::Type type = streamer->getReaderType(); + *this << (int)type; + switch (type) { + case TypeReader::SIMPLE_TYPE: return *this; - case TypeStreamer::LIST_CATEGORY: + case TypeReader::LIST_TYPE: + case TypeReader::SET_TYPE: return *this << streamer->getValueStreamer(); - case TypeStreamer::MAP_CATEGORY: + case TypeReader::MAP_TYPE: return *this << streamer->getKeyStreamer() << streamer->getValueStreamer(); default: @@ -628,35 +629,36 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { reader = TypeReader(typeName, streamer); return *this; } - int category; - *this >> category; - switch (category) { - case TypeStreamer::SIMPLE_CATEGORY: + int type; + *this >> type; + switch (type) { + case TypeReader::SIMPLE_TYPE: reader = TypeReader(typeName, streamer); return *this; - case TypeStreamer::LIST_CATEGORY: { + case TypeReader::LIST_TYPE: + case TypeReader::SET_TYPE: { TypeReader valueReader; *this >> valueReader; - if (streamer && streamer->getCategory() == TypeStreamer::LIST_CATEGORY && + if (streamer && streamer->getReaderType() == type && valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), + reader = TypeReader(typeName, streamer, false, (TypeReader::Type)type, TypeReaderPointer(), TypeReaderPointer(new TypeReader(valueReader))); } return *this; } - case TypeStreamer::MAP_CATEGORY: { + case TypeReader::MAP_TYPE: { TypeReader keyReader, valueReader; *this >> keyReader >> valueReader; - if (streamer && streamer->getCategory() == TypeStreamer::MAP_CATEGORY && + if (streamer && streamer->getReaderType() == TypeReader::MAP_TYPE && keyReader.matchesExactly(streamer->getKeyStreamer()) && valueReader.matchesExactly(streamer->getValueStreamer())) { reader = TypeReader(typeName, streamer); } else { - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(new TypeReader(keyReader)), - TypeReaderPointer(new TypeReader(valueReader))); + reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE, + TypeReaderPointer(new TypeReader(keyReader)), TypeReaderPointer(new TypeReader(valueReader))); } return *this; } @@ -714,20 +716,23 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { // 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(); if (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, + TypeReaderPointer(), TypeReaderPointer(), 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, false, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, + TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } } reader = TypeReader(typeName, streamer); return *this; } - reader = TypeReader(typeName, streamer, false, TypeReaderPointer(), TypeReaderPointer(), fields); + reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE, + TypeReaderPointer(), TypeReaderPointer(), fields); return *this; } @@ -823,11 +828,12 @@ QVector Bitstream::getPropertyReaders(const QMetaObject* metaObj return propertyReaders; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, +TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type, const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector& fields) : _typeName(typeName), _streamer(streamer), _exactMatch(exactMatch), + _type(type), _keyReader(keyReader), _valueReader(valueReader), _fields(fields) { @@ -838,10 +844,28 @@ QVariant TypeReader::read(Bitstream& in) const { return _streamer->read(in); } QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); - if (_valueReader) { - int size; - in >> size; - if (_keyReader) { + switch (_type) { + 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); @@ -849,18 +873,10 @@ QVariant TypeReader::read(Bitstream& in) const { _streamer->insert(object, key, value); } } - } else { - for (int i = 0; i < size; i++) { - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, value); - } - } - } - } else { - foreach (const FieldReader& field, _fields) { - field.read(in, _streamer, object); + break; } + default: + break; } return object; } @@ -870,13 +886,67 @@ void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& refe _streamer->readDelta(in, object, reference); return; } - if (_valueReader) { - // TODO: collection deltas - - } else { - foreach (const FieldReader& field, _fields) { - field.readDelta(in, _streamer, object, reference); - } + bool changed; + in >> changed; + if (!changed) { + object = reference; + return; + } + switch (_type) { + 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; + + 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); + + } + 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; } } @@ -958,8 +1028,8 @@ void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) // nothing by default } -TypeStreamer::Category TypeStreamer::getCategory() const { - return SIMPLE_CATEGORY; +TypeReader::Type TypeStreamer::getReaderType() const { + return TypeReader::SIMPLE_TYPE; } const TypeStreamer* TypeStreamer::getKeyStreamer() const { @@ -977,3 +1047,7 @@ void TypeStreamer::insert(QVariant& object, const QVariant& element) const { void TypeStreamer::insert(QVariant& object, const QVariant& key, const QVariant& value) const { // nothing by default } + +bool TypeStreamer::remove(QVariant& object, const QVariant& key) const { + return false; +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 49b4ddd95f..cfee647d28 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -271,9 +271,15 @@ public: template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); + template void writeDelta(const QList& value, const QList& reference); + template void readDelta(QList& value, const QList& reference); + template void writeDelta(const QSet& value, const QSet& reference); template void readDelta(QSet& value, const QSet& reference); + template void writeDelta(const QHash& value, const QHash& reference); + template void readDelta(QHash& value, const QHash& reference); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -403,12 +409,171 @@ template inline void Bitstream::readDelta(T& value, const T& reference) } } +template inline void Bitstream::writeDelta(const QList& value, const QList& reference) { + if (value == reference) { + *this << false; + return; + } + *this << true; + *this << value.size(); + *this << reference.size(); + for (int i = 0; i < value.size(); i++) { + if (i < reference.size()) { + writeDelta(value.at(i), reference.at(i)); + } else { + *this << value.at(i); + } + } +} + +template inline void Bitstream::readDelta(QList& value, const QList& reference) { + value = reference; + bool changed; + *this >> changed; + if (!changed) { + return; + } + int size, referenceSize; + *this >> size >> referenceSize; + if (size < value.size()) { + value.erase(value.begin() + size, value.end()); + } + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + readDelta(value[i], reference.at(i)); + } else { + T element; + *this >> element; + value.append(element); + } + } +} + template inline void Bitstream::writeDelta(const QSet& value, const QSet& reference) { - + if (value == reference) { + *this << false; + return; + } + *this << true; + int addedOrRemoved = 0; + foreach (const T& element, value) { + if (!reference.contains(element)) { + addedOrRemoved++; + } + } + foreach (const T& element, reference) { + if (!value.contains(element)) { + addedOrRemoved++; + } + } + *this << addedOrRemoved; + foreach (const T& element, value) { + if (!reference.contains(element)) { + *this << element; + } + } + foreach (const T& element, reference) { + if (!value.contains(element)) { + *this << element; + } + } } template inline void Bitstream::readDelta(QSet& value, const QSet& reference) { - + value = reference; + bool changed; + *this >> changed; + if (!changed) { + return; + } + int addedOrRemoved; + *this >> addedOrRemoved; + for (int i = 0; i < addedOrRemoved; i++) { + T element; + *this >> element; + if (!value.remove(element)) { + value.insert(element); + } + } +} + +template inline void Bitstream::writeDelta(const QHash& value, const QHash& reference) { + if (value == reference) { + *this << false; + return; + } + *this << true; + int added = 0; + int modified = 0; + for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { + typename QHash::const_iterator previous = reference.find(it.key()); + if (previous == reference.constEnd()) { + added++; + } else if (previous.value() != it.value()) { + modified++; + } + } + *this << added; + for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { + if (!reference.contains(it.key())) { + *this << it.key(); + *this << it.value(); + } + } + *this << modified; + for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { + typename QHash::const_iterator previous = reference.find(it.key()); + if (previous != reference.constEnd() && previous.value() != it.value()) { + *this << it.key(); + writeDelta(it.value(), previous.value()); + } + } + int removed = 0; + for (typename QHash::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) { + if (!value.contains(it.key())) { + removed++; + } + } + *this << removed; + for (typename QHash::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) { + if (!value.contains(it.key())) { + *this << it.key(); + } + } +} + +template inline void Bitstream::readDelta(QHash& value, const QHash& reference) { + value = reference; + bool changed; + *this >> changed; + if (!changed) { + return; + } + int added; + *this >> added; + for (int i = 0; i < added; i++) { + K key; + V mapping; + *this >> key >> mapping; + value.insert(key, mapping); + } + int modified; + *this >> modified; + for (int i = 0; i < modified; i++) { + K key; + *this >> key; + V& mapping = value[key]; + V newMapping; + readDelta(newMapping, mapping); + mapping = newMapping; + } + int removed; + *this >> removed; + for (int i = 0; i < removed; i++) { + K key; + *this >> key; + value.remove(key); + } } template inline Bitstream& Bitstream::operator<<(const QList& list) { @@ -483,8 +648,11 @@ typedef QSharedPointer TypeReaderPointer; class TypeReader { public: + enum Type { SIMPLE_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; + TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true, - const TypeReaderPointer& keyReader = TypeReaderPointer(), const TypeReaderPointer& valueReader = TypeReaderPointer(), + Type type = SIMPLE_TYPE, const TypeReaderPointer& keyReader = TypeReaderPointer(), + const TypeReaderPointer& valueReader = TypeReaderPointer(), const QVector& fields = QVector()); const QByteArray& getTypeName() const { return _typeName; } @@ -503,6 +671,7 @@ private: QByteArray _typeName; const TypeStreamer* _streamer; bool _exactMatch; + Type _type; TypeReaderPointer _keyReader; TypeReaderPointer _valueReader; QVector _fields; @@ -607,15 +776,14 @@ public: virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(int index, QVariant& object, const QVariant& value) const; - enum Category { SIMPLE_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, MAP_CATEGORY }; - - virtual Category getCategory() const; + virtual TypeReader::Type getReaderType() const; virtual const TypeStreamer* getKeyStreamer() const; virtual const TypeStreamer* getValueStreamer() const; virtual void insert(QVariant& object, const QVariant& value) const; virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const; + virtual bool remove(QVariant& object, const QVariant& key) const; private: @@ -638,7 +806,7 @@ public: template class StreamableTypeStreamer : public SimpleTypeStreamer { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::STREAMABLE_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; } virtual const QVector& getMetaFields() const { return T::getMetaFields(); } virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } virtual void setField(int index, QVariant& object, const QVariant& value) const { @@ -653,7 +821,7 @@ template class CollectionTypeStreamer : public SimpleTypeStreamer { template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } 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()); } @@ -663,21 +831,25 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::SET_TYPE; } 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()); } + virtual bool remove(QVariant& object, const QVariant& key) const { + return static_cast*>(object.data())->remove(key.value()); } }; /// A streamer for hash types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } + virtual TypeReader::Type getReaderType() const { return TypeReader::MAP_TYPE; } 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 { static_cast*>(object.data())->insert(key.value(), value.value()); } + virtual bool remove(QVariant& object, const QVariant& key) const { + return static_cast*>(object.data())->remove(key.value()); } }; /// Macro for registering simple type streamers. diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index e6f1827870..861b7310b8 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -173,6 +173,11 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "}\n"; out << "template<> void Bitstream::writeDelta(const " << name << "& value, const " << name << "& reference) {\n"; + out << " if (value == reference) {\n"; + out << " *this << false;\n"; + out << " return;\n"; + out << " }\n"; + out << " *this << true;\n"; foreach (const QString& base, str.clazz.bases) { out << " writeDelta(static_cast(value), static_cast(reference));\n"; @@ -183,6 +188,12 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "}\n"; out << "template<> void Bitstream::readDelta(" << name << "& value, const " << name << "& reference) {\n"; + out << " bool changed;\n"; + out << " *this >> changed;\n"; + out << " if (!changed) {\n"; + out << " value = reference;\n"; + out << " return;\n"; + out << " }\n"; foreach (const QString& base, str.clazz.bases) { out << " readDelta(static_cast<" << base << "&>(value), static_cast(reference));\n"; From 1b980eebf2dca6ab57643278a4244142f9ac4961 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Mar 2014 14:49:40 -0700 Subject: [PATCH 16/38] More work on delta streaming. --- interface/interface_en.ts | 8 +- libraries/metavoxels/src/Bitstream.cpp | 64 ++++++++++++-- libraries/metavoxels/src/Bitstream.h | 111 +++++++++++++------------ tools/mtc/src/main.cpp | 38 +++++---- 4 files changed, 145 insertions(+), 76 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 34e3614716..334711ea16 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 070ad550d8..888681c4c9 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -903,7 +903,29 @@ void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& refe 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: { @@ -933,7 +955,13 @@ void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& refe 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; @@ -966,12 +994,18 @@ FieldReader::FieldReader(const TypeReader& reader, int index) : void FieldReader::read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const { QVariant value = _reader.read(in); if (_index != -1 && streamer) { - streamer->setField(_index, object, value); + streamer->setField(object, _index, value); } } void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const { - // TODO: field delta + 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, @@ -1024,10 +1058,14 @@ int TypeStreamer::getFieldIndex(const QByteArray& name) const { return -1; } -void TypeStreamer::setField(int index, QVariant& object, const QVariant& value) const { +void TypeStreamer::setField(QVariant& object, int index, const QVariant& value) const { // nothing by default } +QVariant TypeStreamer::getField(const QVariant& object, int index) const { + return QVariant(); +} + TypeReader::Type TypeStreamer::getReaderType() const { return TypeReader::SIMPLE_TYPE; } @@ -1051,3 +1089,19 @@ void TypeStreamer::insert(QVariant& object, const QVariant& key, const QVariant& bool TypeStreamer::remove(QVariant& object, const QVariant& key) const { return false; } + +QVariant TypeStreamer::getValue(const QVariant& object, const QVariant& key) const { + return QVariant(); +} + +void TypeStreamer::prune(QVariant& object, int size) const { + // nothing by default +} + +QVariant TypeStreamer::getValue(const QVariant& object, int index) const { + return QVariant(); +} + +void TypeStreamer::setValue(QVariant& object, int index, const QVariant& value) const { + // nothing by default +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index cfee647d28..4aac34ab1f 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -271,14 +271,17 @@ public: template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); - template void writeDelta(const QList& value, const QList& reference); - template void readDelta(QList& value, const QList& reference); + template void writeRawDelta(const T& value, const T& reference); + template void readRawDelta(T& value, const T& reference); - template void writeDelta(const QSet& value, const QSet& reference); - template void readDelta(QSet& value, const QSet& reference); + template void writeRawDelta(const QList& value, const QList& reference); + template void readRawDelta(QList& value, const QList& reference); - template void writeDelta(const QHash& value, const QHash& reference); - template void readDelta(QHash& value, const QHash& reference); + template void writeRawDelta(const QSet& value, const QSet& reference); + template void readRawDelta(QSet& value, const QSet& reference); + + template void writeRawDelta(const QHash& value, const QHash& reference); + template void readRawDelta(QHash& value, const QHash& reference); Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -395,7 +398,7 @@ template inline void Bitstream::writeDelta(const T& value, const T& ref *this << false; } else { *this << true; - *this << value; + writeRawDelta(value, reference); } } @@ -403,18 +406,29 @@ template inline void Bitstream::readDelta(T& value, const T& reference) bool changed; *this >> changed; if (changed) { - *this >> value; + readRawDelta(value, reference); } else { value = reference; } } -template inline void Bitstream::writeDelta(const QList& value, const QList& reference) { - if (value == reference) { - *this << false; - return; - } - *this << true; +template<> inline void Bitstream::writeDelta(const bool& value, const bool& reference) { + *this << value; +} + +template<> inline void Bitstream::readDelta(bool& value, const bool& reference) { + *this >> value; +} + +template inline void Bitstream::writeRawDelta(const T& value, const T& reference) { + *this << value; +} + +template inline void Bitstream::readRawDelta(T& value, const T& reference) { + *this >> value; +} + +template inline void Bitstream::writeRawDelta(const QList& value, const QList& reference) { *this << value.size(); *this << reference.size(); for (int i = 0; i < value.size(); i++) { @@ -426,13 +440,8 @@ template inline void Bitstream::writeDelta(const QList& value, const } } -template inline void Bitstream::readDelta(QList& value, const QList& reference) { +template inline void Bitstream::readRawDelta(QList& value, const QList& reference) { value = reference; - bool changed; - *this >> changed; - if (!changed) { - return; - } int size, referenceSize; *this >> size >> referenceSize; if (size < value.size()) { @@ -449,12 +458,7 @@ template inline void Bitstream::readDelta(QList& value, const QList< } } -template inline void Bitstream::writeDelta(const QSet& value, const QSet& reference) { - if (value == reference) { - *this << false; - return; - } - *this << true; +template inline void Bitstream::writeRawDelta(const QSet& value, const QSet& reference) { int addedOrRemoved = 0; foreach (const T& element, value) { if (!reference.contains(element)) { @@ -479,13 +483,8 @@ template inline void Bitstream::writeDelta(const QSet& value, const } } -template inline void Bitstream::readDelta(QSet& value, const QSet& reference) { +template inline void Bitstream::readRawDelta(QSet& value, const QSet& reference) { value = reference; - bool changed; - *this >> changed; - if (!changed) { - return; - } int addedOrRemoved; *this >> addedOrRemoved; for (int i = 0; i < addedOrRemoved; i++) { @@ -497,12 +496,7 @@ template inline void Bitstream::readDelta(QSet& value, const QSet } } -template inline void Bitstream::writeDelta(const QHash& value, const QHash& reference) { - if (value == reference) { - *this << false; - return; - } - *this << true; +template inline void Bitstream::writeRawDelta(const QHash& value, const QHash& reference) { int added = 0; int modified = 0; for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { @@ -542,13 +536,8 @@ template inline void Bitstream::writeDelta(const QHash& } } -template inline void Bitstream::readDelta(QHash& value, const QHash& reference) { +template inline void Bitstream::readRawDelta(QHash& value, const QHash& reference) { value = reference; - bool changed; - *this >> changed; - if (!changed) { - return; - } int added; *this >> added; for (int i = 0; i < added; i++) { @@ -774,7 +763,8 @@ public: virtual const QVector& getMetaFields() const; virtual int getFieldIndex(const QByteArray& name) const; - virtual void setField(int index, QVariant& object, const QVariant& value) const; + 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; @@ -784,6 +774,12 @@ public: virtual void insert(QVariant& object, const QVariant& value) const; virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const; virtual bool remove(QVariant& object, const QVariant& key) const; + + virtual QVariant getValue(const QVariant& object, const QVariant& key) const; + + virtual void prune(QVariant& object, int size) const; + virtual QVariant getValue(const QVariant& object, int index) const; + virtual void setValue(QVariant& object, int index, const QVariant& value) const; private: @@ -809,8 +805,10 @@ public: virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; } virtual const QVector& getMetaFields() const { return T::getMetaFields(); } virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } - virtual void setField(int index, QVariant& object, const QVariant& value) const { + virtual void setField(QVariant& object, int index, const QVariant& value) const { static_cast(object.data())->setField(index, value); } + virtual QVariant getField(const QVariant& object, int index) const { + return static_cast(object.constData())->getField(index); } }; /// Base template for collection streamers. @@ -825,6 +823,12 @@ public: 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()); } + virtual void prune(QVariant& object, int size) const { + QList* list = static_cast*>(object.data()); list->erase(list->begin() + size, list->end()); } + virtual QVariant getValue(const QVariant& object, int index) const { + return QVariant::fromValue(static_cast*>(object.constData()).at(index)); } + virtual void setValue(QVariant& object, int index, const QVariant& value) const { + static_cast*>(object.data())->replace(index, value.value()); } }; /// A streamer for set types. @@ -850,6 +854,8 @@ public: static_cast*>(object.data())->insert(key.value(), value.value()); } virtual bool remove(QVariant& object, const QVariant& key) const { return static_cast*>(object.data())->remove(key.value()); } + virtual QVariant getValue(const QVariant& object, const QVariant& key) const { + return QVariant::fromValue(static_cast*>(object.constData())->value(key.value())); } }; /// Macro for registering simple type streamers. @@ -866,8 +872,8 @@ public: #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ - template<> void Bitstream::writeDelta(const X& value, const X& reference); \ - template<> void Bitstream::readDelta(X& value, const X& reference); \ + template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \ + template<> void Bitstream::readRawDelta(X& value, const X& reference); \ bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ static const int* _TypePtr##X = &X::Type; @@ -875,8 +881,8 @@ public: #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ - template<> void Bitstream::writeDelta(const X& value, const X& reference); \ - template<> void Bitstream::readDelta(X& value, const X& reference); \ + template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \ + template<> void Bitstream::readRawDelta(X& value, const X& reference); \ bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ __attribute__((unused)) static const int* _TypePtr##X = &X::Type; @@ -885,8 +891,8 @@ public: #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ - template<> void Bitstream::writeDelta(const X& value, const X& reference); \ - template<> void Bitstream::readDelta(X& value, const X& reference); \ + template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \ + template<> void Bitstream::readRawDelta(X& value, const X& reference); \ bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ static const int* _TypePtr##X = &X::Type; \ @@ -906,6 +912,7 @@ template int registerStreamableMetaType() { static const QVector& getMetaFields(); \ static int getFieldIndex(const QByteArray& name); \ void setField(int index, const QVariant& value); \ + QVariant getField(int index) const; \ private: \ static QHash createFieldIndices(); diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 861b7310b8..9c77851961 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -152,6 +152,25 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << " }\n"; out << "}\n"; + out << "QVariant " << name << "::getField(int index) const {\n"; + if (!str.clazz.bases.isEmpty()) { + out << " int nextIndex;\n"; + } + foreach (const QString& base, str.clazz.bases) { + out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n"; + out << " return " << base << "::getField(index);\n"; + out << " }\n"; + out << " index = nextIndex;\n"; + } + out << " switch (index) {\n"; + for (int i = 0; i < str.fields.size(); i++) { + out << " case " << i << ":\n"; + out << " return QVariant::fromValue(this->" << str.fields.at(i).name << ");\n"; + } + out << " }\n"; + out << " return QVariant();\n"; + out << "}\n"; + out << "Bitstream& operator<<(Bitstream& out, const " << name << "& obj) {\n"; foreach (const QString& base, str.clazz.bases) { out << " out << static_cast(obj);\n"; @@ -172,14 +191,9 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << " return in;\n"; out << "}\n"; - out << "template<> void Bitstream::writeDelta(const " << name << "& value, const " << name << "& reference) {\n"; - out << " if (value == reference) {\n"; - out << " *this << false;\n"; - out << " return;\n"; - out << " }\n"; - out << " *this << true;\n"; + out << "template<> void Bitstream::writeRawDelta(const " << name << "& value, const " << name << "& reference) {\n"; foreach (const QString& base, str.clazz.bases) { - out << " writeDelta(static_cast(value), static_cast(value), static_cast(reference));\n"; } foreach (const Field& field, str.fields) { @@ -187,15 +201,9 @@ void generateOutput (QTextStream& out, const QList& streamables) { } out << "}\n"; - out << "template<> void Bitstream::readDelta(" << name << "& value, const " << name << "& reference) {\n"; - out << " bool changed;\n"; - out << " *this >> changed;\n"; - out << " if (!changed) {\n"; - out << " value = reference;\n"; - out << " return;\n"; - out << " }\n"; + out << "template<> void Bitstream::readRawDelta(" << name << "& value, const " << name << "& reference) {\n"; foreach (const QString& base, str.clazz.bases) { - out << " readDelta(static_cast<" << base << "&>(value), static_cast(value), static_cast(reference));\n"; } foreach (const Field& field, str.fields) { From b8414dfb19eeb4c167263d473a7abc458e73d5d5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Mar 2014 18:20:26 -0700 Subject: [PATCH 17/38] More work on delta streaming. --- libraries/metavoxels/src/Bitstream.cpp | 80 ++++++++++++++++++++++++++ libraries/metavoxels/src/Bitstream.h | 19 +++--- 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 888681c4c9..324bff6071 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -223,6 +223,68 @@ void Bitstream::clearSharedObject(int id) { } } +void Bitstream::writeDelta(bool value, bool reference) { + *this << value; +} + +void Bitstream::readDelta(bool& value, bool reference) { + *this >> value; +} + +void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { + if (!value) { + _metaObjectStreamer << NULL; + return; + } + const QMetaObject* metaObject = value->metaObject(); + _metaObjectStreamer << metaObject; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored(value)) { + continue; + } + const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + if (streamer) { + streamer->writeDelta(*this, property.read(value), reference && metaObject == reference->metaObject() ? + property.read(reference) : QVariant()); + } + } +} + +void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { + ObjectReader objectReader; + _metaObjectStreamer >> objectReader; + value = objectReader.readDelta(*this, reference); +} + +void Bitstream::writeRawDelta(const SharedObjectPointer& value, const SharedObjectPointer& reference) { + if (!value) { + *this << (int)0; + return; + } + *this << value->getID(); + writeRawDelta((const QObject*)value.data(), (const QObject*)reference.data()); +} + +void Bitstream::readRawDelta(SharedObjectPointer& value, const SharedObjectPointer& reference) { + int id; + *this >> id; + if (id == 0) { + value = SharedObjectPointer(); + return; + } + QPointer& sharedObject = _weakSharedObjectHash[id]; + if (!sharedObject) { + QObject* object; + readRawDelta(object, (const QObject*)reference.data()); + sharedObject = static_cast(object); + return; + } + ObjectReader objectReader; + _metaObjectStreamer >> objectReader; + objectReader.readDelta(*this, reference, sharedObject.data()); +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -1025,6 +1087,16 @@ QObject* ObjectReader::read(Bitstream& in, QObject* object) const { 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); } @@ -1041,6 +1113,14 @@ 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()); + if (_property.isValid() && object) { + _property.write(object, value); + } +} + 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 4aac34ab1f..1b748a3ea9 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -268,8 +268,17 @@ public: /// Removes a shared object from the read mappings. void clearSharedObject(int id); + void writeDelta(bool value, bool reference); + void readDelta(bool& value, bool reference); + template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); + + void writeRawDelta(const QObject* value, const QObject* reference); + void readRawDelta(QObject*& value, const QObject* reference); + + void writeRawDelta(const SharedObjectPointer& value, const SharedObjectPointer& reference); + void readRawDelta(SharedObjectPointer& value, const SharedObjectPointer& reference); template void writeRawDelta(const T& value, const T& reference); template void readRawDelta(T& value, const T& reference); @@ -412,14 +421,6 @@ template inline void Bitstream::readDelta(T& value, const T& reference) } } -template<> inline void Bitstream::writeDelta(const bool& value, const bool& reference) { - *this << value; -} - -template<> inline void Bitstream::readDelta(bool& value, const bool& reference) { - *this >> value; -} - template inline void Bitstream::writeRawDelta(const T& value, const T& reference) { *this << value; } @@ -697,6 +698,7 @@ public: 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; } @@ -719,6 +721,7 @@ public: const TypeReader& getReader() const { return _reader; } void read(Bitstream& in, QObject* object) const; + void readDelta(Bitstream& in, QObject* object, const QObject* reference) const; private: From 5b4478328c541d183a6cecb3d25fd6c34a815d17 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sat, 22 Mar 2014 23:43:42 -0700 Subject: [PATCH 18/38] More delta streaming work. --- libraries/metavoxels/src/Bitstream.cpp | 81 ++++++++++++++------------ libraries/metavoxels/src/Bitstream.h | 9 ++- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 324bff6071..bb2d43335f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -185,9 +185,16 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { // find out when shared objects are deleted in order to clear their mappings for (QHash::const_iterator it = mappings.sharedObjectOffsets.constBegin(); it != mappings.sharedObjectOffsets.constEnd(); it++) { - if (it.key()) { - connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*))); + if (!it.key()) { + continue; } + connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*))); + QPointer& reference = _sharedObjectReferences[it.key()->getID()]; + if (reference) { + _sharedObjectStreamer.removePersistentID(reference); + reference->disconnect(this); + } + reference = it.key(); } } @@ -210,6 +217,19 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { _attributeStreamer.persistTransientValues(mappings.attributeValues); _scriptStringStreamer.persistTransientValues(mappings.scriptStringValues); _sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues); + + for (QHash::const_iterator it = mappings.sharedObjectValues.constBegin(); + it != mappings.sharedObjectValues.constEnd(); it++) { + if (!it.value()) { + continue; + } + QPointer& reference = _sharedObjectReferences[it.value()->getID()]; + if (reference) { + _sharedObjectStreamer.removePersistentValue(reference.data()); + } + reference = it.value(); + _weakSharedObjectHash.remove(it.value()->getID()); + } } void Bitstream::persistAndResetReadMappings() { @@ -257,34 +277,6 @@ void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { value = objectReader.readDelta(*this, reference); } -void Bitstream::writeRawDelta(const SharedObjectPointer& value, const SharedObjectPointer& reference) { - if (!value) { - *this << (int)0; - return; - } - *this << value->getID(); - writeRawDelta((const QObject*)value.data(), (const QObject*)reference.data()); -} - -void Bitstream::readRawDelta(SharedObjectPointer& value, const SharedObjectPointer& reference) { - int id; - *this >> id; - if (id == 0) { - value = SharedObjectPointer(); - return; - } - QPointer& sharedObject = _weakSharedObjectHash[id]; - if (!sharedObject) { - QObject* object; - readRawDelta(object, (const QObject*)reference.data()); - sharedObject = static_cast(object); - return; - } - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - objectReader.readDelta(*this, reference, sharedObject.data()); -} - Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -824,7 +816,14 @@ Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { if (!object) { return *this << (int)0; } - return *this << object->getID() << (QObject*)object.data(); + *this << object->getID(); + QPointer reference = _sharedObjectReferences.value(object->getID()); + if (reference) { + writeRawDelta((QObject*)object.data(), (QObject*)reference.data()); + } else { + *this << (QObject*)object.data(); + } + return *this; } Bitstream& Bitstream::operator>(SharedObjectPointer& object) { @@ -834,15 +833,23 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { object = SharedObjectPointer(); return *this; } + QPointer reference = _sharedObjectReferences.value(id); QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { ObjectReader objectReader; _metaObjectStreamer >> objectReader; - objectReader.read(*this, pointer.data()); - + if (reference) { + objectReader.readDelta(*this, reference.data(), pointer.data()); + } else { + objectReader.read(*this, pointer.data()); + } } else { - QObject* rawObject; - *this >> rawObject; + QObject* rawObject; + if (reference) { + readRawDelta(rawObject, (QObject*)reference.data()); + } else { + *this >> rawObject; + } pointer = static_cast(rawObject); pointer->setRemoteID(id); } @@ -851,7 +858,9 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } void Bitstream::clearSharedObject(QObject* object) { - int id = _sharedObjectStreamer.takePersistentID(static_cast(object)); + SharedObject* sharedObject = static_cast(object); + _sharedObjectReferences.remove(sharedObject->getID()); + int id = _sharedObjectStreamer.takePersistentID(sharedObject); if (id != 0) { emit sharedObjectCleared(id); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 1b748a3ea9..6b70241519 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -75,8 +75,12 @@ public: void persistTransientValues(const QHash& transientValues); + void removePersistentID(P value) { _persistentIDs.remove(value); } + int takePersistentID(P value) { return _persistentIDs.take(value); } + void removePersistentValue(V value) { int id = _valueIDs.take(value); _persistentValues.remove(id); } + V takePersistentValue(int id) { V value = _persistentValues.take(id); _valueIDs.remove(value); return value; } RepeatedValueStreamer& operator<<(K value); @@ -277,9 +281,6 @@ public: void writeRawDelta(const QObject* value, const QObject* reference); void readRawDelta(QObject*& value, const QObject* reference); - void writeRawDelta(const SharedObjectPointer& value, const SharedObjectPointer& reference); - void readRawDelta(SharedObjectPointer& value, const SharedObjectPointer& reference); - template void writeRawDelta(const T& value, const T& reference); template void readRawDelta(T& value, const T& reference); @@ -390,6 +391,8 @@ private: RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; + + WeakSharedObjectHash _sharedObjectReferences; WeakSharedObjectHash _weakSharedObjectHash; From 91c10a4f3ad8556571421bf3d0da4080166ce939 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 24 Mar 2014 11:56:58 -0700 Subject: [PATCH 19/38] Delta streaming for variants. --- libraries/metavoxels/src/Bitstream.cpp | 28 +++++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 15 ++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index bb2d43335f..059a457e5e 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -251,6 +251,24 @@ void Bitstream::readDelta(bool& value, bool reference) { *this >> value; } +void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) { + // QVariant only handles == for built-in types; we need to use our custom operators + const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); + if (value.userType() == reference.userType() && (!streamer || streamer->equal(value, reference))) { + *this << false; + return; + } + *this << true; + _typeStreamerStreamer << streamer; + streamer->writeRawDelta(*this, value, reference); +} + +void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { + TypeReader typeReader; + _typeStreamerStreamer >> typeReader; + typeReader.readRawDelta(*this, value, reference); +} + void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { if (!value) { _metaObjectStreamer << NULL; @@ -959,8 +977,16 @@ void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& refe } bool changed; in >> changed; - if (!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) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 6b70241519..a6826dd60c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -275,9 +275,13 @@ public: void writeDelta(bool value, bool reference); void readDelta(bool& value, bool reference); + void writeDelta(const QVariant& value, const QVariant& reference); + template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); + void readRawDelta(QVariant& value, const QVariant& reference); + void writeRawDelta(const QObject* value, const QObject* reference); void readRawDelta(QObject*& value, const QObject* reference); @@ -653,6 +657,7 @@ public: 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; @@ -761,12 +766,17 @@ public: void setType(int type) { _type = type; } int getType() const { return _type; } + virtual bool equal(const QVariant& first, const QVariant& second) const = 0; + virtual void write(Bitstream& out, const QVariant& value) const = 0; virtual QVariant read(Bitstream& in) const = 0; 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 writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0; + virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0; + virtual const QVector& getMetaFields() const; virtual int getFieldIndex(const QByteArray& name) const; virtual void setField(QVariant& object, int index, const QVariant& value) const; @@ -796,12 +806,17 @@ private: template class SimpleTypeStreamer : public TypeStreamer { public: + virtual bool equal(const QVariant& first, const QVariant& second) const { return first.value() == second.value(); } virtual void write(Bitstream& out, const QVariant& value) const { out << value.value(); } virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { out.writeDelta(value.value(), reference.value()); } virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { in.readDelta(*static_cast(value.data()), reference.value()); } + virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + out.writeRawDelta(value.value(), reference.value()); } + virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + in.readRawDelta(*static_cast(value.data()), reference.value()); } }; /// A streamer for types compiled by mtc. From 5934a7fbea1209efa43488a36215067b82a73d75 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Mar 2014 20:41:19 -0700 Subject: [PATCH 20/38] Working on voxelizing spanners. --- interface/interface_en.ts | 16 ++-- interface/src/MetavoxelSystem.cpp | 39 ++++++++-- interface/src/ui/MetavoxelEditor.cpp | 1 - .../metavoxels/src/AttributeRegistry.cpp | 46 +++++++++-- libraries/metavoxels/src/AttributeRegistry.h | 53 +++++++------ libraries/metavoxels/src/MetavoxelData.cpp | 77 ++++++++++++++++--- libraries/metavoxels/src/MetavoxelData.h | 26 +++++-- .../metavoxels/src/MetavoxelMessages.cpp | 50 ++++++------ 8 files changed, 220 insertions(+), 88 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 689b45afcf..f5c7f225df 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 23e50176eb..a003f650f5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -168,7 +168,9 @@ void MetavoxelSystem::maybeAttachClient(const SharedNodePointer& node) { MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector() << AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute()), + AttributeRegistry::getInstance()->getNormalAttribute() << + AttributeRegistry::getInstance()->getSpannerColorAttribute() << + AttributeRegistry::getInstance()->getSpannerNormalAttribute()), _points(points) { } @@ -186,11 +188,36 @@ int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { QRgb color = info.inputValues.at(0).getInlineValue(); QRgb normal = info.inputValues.at(1).getInlineValue(); quint8 alpha = qAlpha(color); - if (alpha > 0) { - Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), - { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha }, - { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; - _points.append(point); + if (info.inputValues.at(4).getAttribute()) { + if (alpha > 0) { + Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), + { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha }, + { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; + _points.append(point); + } + } else { + QRgb spannerColor = info.inputValues.at(2).getInlineValue(); + QRgb spannerNormal = info.inputValues.at(3).getInlineValue(); + quint8 spannerAlpha = qAlpha(spannerColor); + if (spannerAlpha > 0) { + if (alpha > 0) { + Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), + { quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha }, + { quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } }; + _points.append(point); + + } else { + Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), + { quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha }, + { quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } }; + _points.append(point); + } + } else if (alpha > 0) { + Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), + { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha }, + { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; + _points.append(point); + } } return STOP_RECURSION; } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index eed8a7b6f3..7a70cb9a4c 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -655,6 +655,5 @@ bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const { } QVariant SetSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { - static_cast(spanner.data())->setGranularity(_editor->getGridSpacing()); return QVariant::fromValue(SetSpannerEdit(spanner)); } diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 634e7d3b57..c61a8e7560 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -30,7 +30,9 @@ AttributeRegistry::AttributeRegistry() : SharedObjectPointer(new DefaultMetavoxelGuide())))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), - _normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))) { + _normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))), + _spannerColorAttribute(registerAttribute(new QRgbAttribute("spannerColor"))), + _spannerNormalAttribute(registerAttribute(new PackedNormalAttribute("spannerNormal", qRgb(0, 127, 0)))) { // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f; @@ -92,6 +94,10 @@ bool AttributeValue::isDefault() const { return !_attribute || _attribute->equal(_value, _attribute->getDefaultValue()); } +AttributeValue AttributeValue::split() const { + return _attribute ? _attribute->split(*this) : AttributeValue(); +} + bool AttributeValue::operator==(const AttributeValue& other) const { return _attribute == other._attribute && (!_attribute || _attribute->equal(_value, other._value)); } @@ -130,6 +136,14 @@ OwnedAttributeValue::~OwnedAttributeValue() { } } +void OwnedAttributeValue::mix(const AttributeValue& first, const AttributeValue& second, float alpha) { + if (_attribute) { + _attribute->destroy(_value); + } + _attribute = first.getAttribute(); + _value = _attribute->mix(first.getValue(), second.getValue(), alpha); +} + OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) { if (_attribute) { _attribute->destroy(_value); @@ -206,6 +220,16 @@ bool QRgbAttribute::merge(void*& parent, void* children[]) const { return allChildrenEqual; } +void* QRgbAttribute::mix(void* first, void* second, float alpha) const { + QRgb firstValue = decodeInline(first); + QRgb secondValue = decodeInline(second); + return encodeInline(qRgba( + glm::mix((float)qRed(firstValue), (float)qRed(secondValue), alpha), + glm::mix((float)qGreen(firstValue), (float)qGreen(secondValue), alpha), + glm::mix((float)qBlue(firstValue), (float)qBlue(secondValue), alpha), + glm::mix((float)qAlpha(firstValue), (float)qAlpha(secondValue), alpha))); +} + void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return encodeInline((QRgb)value.toUInt32()); } @@ -232,21 +256,23 @@ PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultVa bool PackedNormalAttribute::merge(void*& parent, void* children[]) const { QRgb firstValue = decodeInline(children[0]); - int totalRed = (char)qRed(firstValue); - int totalGreen = (char)qGreen(firstValue); - int totalBlue = (char)qBlue(firstValue); + glm::vec3 total = unpackNormal(firstValue); bool allChildrenEqual = true; for (int i = 1; i < Attribute::MERGE_COUNT; i++) { QRgb value = decodeInline(children[i]); - totalRed += (char)qRed(value); - totalGreen += (char)qGreen(value); - totalBlue += (char)qBlue(value); + total += unpackNormal(value); allChildrenEqual &= (firstValue == value); } - parent = encodeInline(packNormal(glm::normalize(glm::vec3(totalRed, totalGreen, totalBlue)))); + parent = encodeInline(packNormal(glm::normalize(total))); return allChildrenEqual; } +void* PackedNormalAttribute::mix(void* first, void* second, float alpha) const { + glm::vec3 firstNormal = unpackNormal(decodeInline(first)); + glm::vec3 secondNormal = unpackNormal(decodeInline(second)); + return encodeInline(packNormal(glm::normalize(glm::mix(firstNormal, secondNormal, alpha)))); +} + const float CHAR_SCALE = 127.0f; const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE; @@ -290,6 +316,10 @@ bool SharedObjectAttribute::merge(void*& parent, void* children[]) const { return true; } +AttributeValue SharedObjectSetAttribute::split(const AttributeValue& parent) const { + return AttributeValue(); +} + void* SharedObjectAttribute::createFromVariant(const QVariant& value) const { return create(encodeInline(value.value())); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index f3dd9f4632..e0a1c0b3f0 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -71,9 +71,15 @@ public: /// Returns a reference to the standard QRgb "color" attribute. const AttributePointer& getColorAttribute() const { return _colorAttribute; } - /// Returns a reference to the standard QRgb "normal" attribute. + /// Returns a reference to the standard packed normal "normal" attribute. const AttributePointer& getNormalAttribute() const { return _normalAttribute; } + /// Returns a reference to the standard QRgb "spannerColor" attribute. + const AttributePointer& getSpannerColorAttribute() const { return _spannerColorAttribute; } + + /// Returns a reference to the standard packed normal "spannerNormal" attribute. + const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -83,6 +89,8 @@ private: AttributePointer _spannersAttribute; AttributePointer _colorAttribute; AttributePointer _normalAttribute; + AttributePointer _spannerColorAttribute; + AttributePointer _spannerNormalAttribute; }; /// Converts a value to a void pointer. @@ -107,13 +115,14 @@ public: template void setInlineValue(T value) { _value = encodeInline(value); } template T getInlineValue() const { return decodeInline(_value); } - - template T* getPointerValue() const { return static_cast(_value); } + template T getSafeInlineValue() const { return _attribute ? decodeInline(_value) : T(); } void* copy() const; bool isDefault() const; + AttributeValue split() const; + bool operator==(const AttributeValue& other) const; bool operator==(void* other) const; @@ -145,6 +154,9 @@ public: /// Destroys the current value, if any. ~OwnedAttributeValue(); + /// Sets this attribute to a mix of the first and second provided. + void mix(const AttributeValue& first, const AttributeValue& second, float alpha); + /// Destroys the current value, if any, and copies the specified other value. OwnedAttributeValue& operator=(const AttributeValue& other); @@ -196,6 +208,12 @@ public: /// \return whether or not the children and parent values are all equal virtual bool merge(void*& parent, void* children[]) const = 0; + /// Returns the attribute value to pass to children below leaves (either the parent, or the default, or a null value). + virtual AttributeValue split(const AttributeValue& parent) const { return parent; } + + /// Mixes the first and the second, returning a new value with the result. + virtual void* mix(void* first, void* second, float alpha) const = 0; + virtual void* getDefaultValue() const = 0; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } @@ -225,6 +243,8 @@ public: virtual bool equal(void* first, void* second) const { return decodeInline(first) == decodeInline(second); } + virtual void* mix(void* first, void* second, float alpha) const { return create(alpha < 0.5f ? first : second); } + virtual void* getDefaultValue() const { return encodeInline(_defaultValue); } protected: @@ -245,27 +265,6 @@ template inline void InlineAttribute::write(Bitstrea } } -/// Provides merging using the =, ==, += and /= operators. -template class SimpleInlineAttribute : public InlineAttribute { -public: - - SimpleInlineAttribute(const QString& name, T defaultValue = T()) : InlineAttribute(name, defaultValue) { } - - virtual bool merge(void*& parent, void* children[]) const; -}; - -template inline bool SimpleInlineAttribute::merge(void*& parent, void* children[]) const { - T& merged = *(T*)&parent; - merged = decodeInline(children[0]); - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - merged += decodeInline(children[i]); - allChildrenEqual &= (decodeInline(children[0]) == decodeInline(children[i])); - } - merged /= Attribute::MERGE_COUNT; - return allChildrenEqual; -} - /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { Q_OBJECT @@ -277,6 +276,8 @@ public: virtual bool merge(void*& parent, void* children[]) const; + virtual void* mix(void* first, void* second, float alpha) const; + virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; virtual void* createFromVariant(const QVariant& value) const; @@ -293,6 +294,8 @@ public: Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); virtual bool merge(void*& parent, void* children[]) const; + + virtual void* mix(void* first, void* second, float alpha) const; }; /// Packs a normal into an RGB value. @@ -343,6 +346,8 @@ public: virtual bool merge(void*& parent, void* children[]) const; + virtual AttributeValue split(const AttributeValue& parent) const; + virtual QWidget* createEditor(QWidget* parent = NULL) const; private: diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 397e10d45f..c561cbae09 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -169,7 +169,7 @@ template int SpannerUpdateVisitor::visit(MetavoxelIn if (info.size > _longestSide) { return DEFAULT_ORDER; } - SharedObjectSet set = info.inputValues.at(0).getInlineValue(); + SharedObjectSet set = info.inputValues.at(0).getSafeInlineValue(); F(set, _object); info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); return STOP_RECURSION; @@ -177,7 +177,7 @@ template int SpannerUpdateVisitor::visit(MetavoxelIn void MetavoxelData::insert(const AttributePointer& attribute, const SharedObjectPointer& object) { Spanner* spanner = static_cast(object.data()); - insert(attribute, spanner->getBounds(), spanner->getGranularity(), object); + insert(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object); } void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, @@ -192,7 +192,7 @@ void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, void MetavoxelData::remove(const AttributePointer& attribute, const SharedObjectPointer& object) { Spanner* spanner = static_cast(object.data()); - remove(attribute, spanner->getBounds(), spanner->getGranularity(), object); + remove(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object); } void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, @@ -203,7 +203,7 @@ void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, void MetavoxelData::toggle(const AttributePointer& attribute, const SharedObjectPointer& object) { Spanner* spanner = static_cast(object.data()); - toggle(attribute, spanner->getBounds(), spanner->getGranularity(), object); + toggle(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object); } void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds, @@ -875,7 +875,7 @@ void SpannerVisitor::prepare() { int SpannerVisitor::visit(MetavoxelInfo& info) { for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { - foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { + foreach (const SharedObjectPointer& object, info.inputValues.at(i).getSafeInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { if (!visit(spanner)) { @@ -927,7 +927,7 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { - foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { + foreach (const SharedObjectPointer& object, info.inputValues.at(i).getSafeInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { SpannerDistance spannerDistance = { spanner }; @@ -991,7 +991,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { MetavoxelNode* child = (node && (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? - child->getAttributeValue(parentValue.getAttribute()) : parentValue; + child->getAttributeValue(parentValue.getAttribute()) : parentValue.split(); } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); @@ -1202,11 +1202,13 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { return AttributeValue(visitor.getOutputs().at(index)); } -const float DEFAULT_GRANULARITY = 0.01f; +const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; +const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); Spanner::Spanner() : _renderer(NULL), - _granularity(DEFAULT_GRANULARITY), + _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), + _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), _lastVisit(0) { } @@ -1223,10 +1225,19 @@ const QVector& Spanner::getAttributes() const { return emptyVector; } +const QVector& Spanner::getVoxelizedAttributes() const { + static QVector emptyVector; + return emptyVector; +} + bool Spanner::getAttributeValues(MetavoxelInfo& info) const { return false; } +bool Spanner::blendAttributeValues(MetavoxelInfo& info) const { + return false; +} + bool Spanner::testAndSetVisited() { if (_lastVisit == _visit) { return false; @@ -1320,6 +1331,13 @@ const QVector& Sphere::getAttributes() const { return attributes; } +const QVector& Sphere::getVoxelizedAttributes() const { + static QVector attributes = QVector() << + AttributeRegistry::getInstance()->getSpannerColorAttribute() << + AttributeRegistry::getInstance()->getSpannerNormalAttribute(); + return attributes; +} + bool Sphere::getAttributeValues(MetavoxelInfo& info) const { // bounds check Box bounds = info.getBounds(); @@ -1339,7 +1357,7 @@ bool Sphere::getAttributeValues(MetavoxelInfo& info) const { getNormal(info); return false; } - if (info.size <= getGranularity()) { + if (info.size <= getVoxelizationGranularity()) { // best guess if (pointsWithin > 0) { info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(qRgba( @@ -1351,6 +1369,41 @@ bool Sphere::getAttributeValues(MetavoxelInfo& info) const { return true; } +bool Sphere::blendAttributeValues(MetavoxelInfo& info) const { + // bounds check + Box bounds = info.getBounds(); + if (!getBounds().intersects(bounds)) { + return false; + } + // count the points inside the sphere + int pointsWithin = 0; + for (int i = 0; i < Box::VERTEX_COUNT; i++) { + if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { + pointsWithin++; + } + } + if (pointsWithin == Box::VERTEX_COUNT) { + // entirely contained + info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); + info.outputValues[1] = getNormal(info); + return false; + } + if (info.size <= getVoxelizationGranularity()) { + // best guess + if (pointsWithin > 0) { + int oldAlpha = qAlpha(info.inputValues.at(0).getInlineValue()); + int newAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; + float combinedAlpha = (float)newAlpha / (oldAlpha + newAlpha); + info.outputValues[0].mix(info.inputValues.at(0), AttributeValue(getAttributes().at(0), + encodeInline(qRgba(_color.red(), _color.green(), _color.blue(), + _color.alpha() * pointsWithin / Box::VERTEX_COUNT))), combinedAlpha); + info.outputValues[1].mix(info.inputValues.at(1), getNormal(info), combinedAlpha); + } + return false; + } + return true; +} + bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); } @@ -1364,7 +1417,7 @@ void Sphere::updateBounds() { setBounds(Box(getTranslation() - extent, getTranslation() + extent)); } -void Sphere::getNormal(MetavoxelInfo& info) const { +AttributeValue Sphere::getNormal(MetavoxelInfo& info) const { glm::vec3 normal = info.getCenter() - getTranslation(); float length = glm::length(normal); QRgb color; @@ -1379,7 +1432,7 @@ void Sphere::getNormal(MetavoxelInfo& info) const { const QRgb DEFAULT_NORMAL = 0x007F00; color = DEFAULT_NORMAL; } - info.outputValues[1] = AttributeValue(getAttributes().at(1), encodeInline(color)); + return AttributeValue(getAttributes().at(1), encodeInline(color)); } StaticModel::StaticModel() { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 9d642f8f34..3e7c6f1b15 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -408,8 +408,9 @@ public: class Spanner : public SharedObject { Q_OBJECT Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) - Q_PROPERTY(float granularity MEMBER _granularity DESIGNABLE false) - + Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) + Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) + public: /// Increments the value of the global visit counter. @@ -420,16 +421,26 @@ public: void setBounds(const Box& bounds); const Box& getBounds() const { return _bounds; } - void setGranularity(float granularity) { _granularity = granularity; } - float getGranularity() const { return _granularity; } + void setPlacementGranularity(float granularity) { _placementGranularity = granularity; } + float getPlacementGranularity() const { return _placementGranularity; } + + void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } + float getVoxelizationGranularity() const { return _voxelizationGranularity; } /// Returns a reference to the list of attributes associated with this spanner. virtual const QVector& getAttributes() const; + /// Returns a reference to the list of corresponding attributes that we voxelize the spanner into. + virtual const QVector& getVoxelizedAttributes() const; + /// Sets the attribute values associated with this spanner in the supplied info. /// \return true to recurse, false to stop virtual bool getAttributeValues(MetavoxelInfo& info) const; + /// Blends the attribute values associated with this spanner into the supplied info. + /// \return true to recurse, false to stop + virtual bool blendAttributeValues(MetavoxelInfo& info) const; + /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(); @@ -455,7 +466,8 @@ protected: private: Box _bounds; - float _granularity; + float _placementGranularity; + float _voxelizationGranularity; int _lastVisit; ///< the identifier of the last visit static int _visit; ///< the global visit counter @@ -521,7 +533,9 @@ public: const QColor& getColor() const { return _color; } virtual const QVector& getAttributes() const; + virtual const QVector& getVoxelizedAttributes() const; virtual bool getAttributeValues(MetavoxelInfo& info) const; + virtual bool blendAttributeValues(MetavoxelInfo& info) const; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; signals: @@ -538,7 +552,7 @@ private slots: private: - void getNormal(MetavoxelInfo& info) const; + AttributeValue getNormal(MetavoxelInfo& info) const; QColor _color; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 7a60ae6263..b600ef87bf 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -104,8 +104,33 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } +class SetSpannerEditVisitor : public MetavoxelVisitor { +public: + + SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner); + + virtual int visit(MetavoxelInfo& info); + +private: + + Spanner* _spanner; +}; + +SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : + MetavoxelVisitor(attributes, attributes), + _spanner(spanner) { +} + +int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { + return _spanner->blendAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; +} + void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.insert(attribute, spanner); + data.insert(attribute, this->spanner); + + Spanner* spanner = static_cast(this->spanner.data()); + SetSpannerEditVisitor visitor(spanner->getVoxelizedAttributes(), spanner); + data.guide(visitor); } RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : @@ -130,27 +155,6 @@ void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o data.clear(attribute); } -class SetSpannerEditVisitor : public MetavoxelVisitor { -public: - - SetSpannerEditVisitor(Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - Spanner* _spanner; -}; - -SetSpannerEditVisitor::SetSpannerEditVisitor(Spanner* spanner) : - MetavoxelVisitor(spanner->getAttributes(), spanner->getAttributes()), - _spanner(spanner) { -} - -int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { - return _spanner->getAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; -} - SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : spanner(spanner) { } @@ -163,6 +167,6 @@ void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& obje data.expand(); } - SetSpannerEditVisitor visitor(spanner); + SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner); data.guide(visitor); } From 48385ccc322982cc0c67f525960665baf2ed13ae Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 13:17:41 -0700 Subject: [PATCH 21/38] Fixed Xcode compile error. --- libraries/metavoxels/src/Bitstream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index a6826dd60c..760154a928 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -847,7 +847,7 @@ public: virtual void prune(QVariant& object, int size) const { QList* list = static_cast*>(object.data()); list->erase(list->begin() + size, list->end()); } virtual QVariant getValue(const QVariant& object, int index) const { - return QVariant::fromValue(static_cast*>(object.constData()).at(index)); } + return QVariant::fromValue(static_cast*>(object.constData())->at(index)); } virtual void setValue(QVariant& object, int index, const QVariant& value) const { static_cast*>(object.data())->replace(index, value.value()); } }; From fd5f378e1c81491c46e748beb214a6cb64d1ae08 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Mar 2014 11:16:55 -0700 Subject: [PATCH 22/38] Better color/normal merging. --- .../metavoxels/src/AttributeRegistry.cpp | 39 +++++++++---------- libraries/metavoxels/src/AttributeRegistry.h | 7 ---- libraries/metavoxels/src/MetavoxelData.cpp | 6 +-- libraries/metavoxels/src/MetavoxelData.h | 1 + 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c61a8e7560..c41420d7c6 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -30,9 +30,9 @@ AttributeRegistry::AttributeRegistry() : SharedObjectPointer(new DefaultMetavoxelGuide())))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), - _normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))), + _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), _spannerColorAttribute(registerAttribute(new QRgbAttribute("spannerColor"))), - _spannerNormalAttribute(registerAttribute(new PackedNormalAttribute("spannerNormal", qRgb(0, 127, 0)))) { + _spannerNormalAttribute(registerAttribute(new PackedNormalAttribute("spannerNormal"))) { // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f; @@ -94,10 +94,6 @@ bool AttributeValue::isDefault() const { return !_attribute || _attribute->equal(_value, _attribute->getDefaultValue()); } -AttributeValue AttributeValue::split() const { - return _attribute ? _attribute->split(*this) : AttributeValue(); -} - bool AttributeValue::operator==(const AttributeValue& other) const { return _attribute == other._attribute && (!_attribute || _attribute->equal(_value, other._value)); } @@ -199,24 +195,29 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : InlineAttribute(name, defaultValue) { } - + bool QRgbAttribute::merge(void*& parent, void* children[]) const { QRgb firstValue = decodeInline(children[0]); - int totalRed = qRed(firstValue); - int totalGreen = qGreen(firstValue); - int totalBlue = qBlue(firstValue); int totalAlpha = qAlpha(firstValue); + int totalRed = qRed(firstValue) * totalAlpha; + int totalGreen = qGreen(firstValue) * totalAlpha; + int totalBlue = qBlue(firstValue) * totalAlpha; bool allChildrenEqual = true; for (int i = 1; i < Attribute::MERGE_COUNT; i++) { QRgb value = decodeInline(children[i]); - totalRed += qRed(value); - totalGreen += qGreen(value); - totalBlue += qBlue(value); - totalAlpha += qAlpha(value); + int alpha = qAlpha(value); + totalRed += qRed(value) * alpha; + totalGreen += qGreen(value) * alpha; + totalBlue += qBlue(value) * alpha; + totalAlpha += alpha; allChildrenEqual &= (firstValue == value); } - parent = encodeInline(qRgba(totalRed / MERGE_COUNT, totalGreen / MERGE_COUNT, - totalBlue / MERGE_COUNT, totalAlpha / MERGE_COUNT)); + if (totalAlpha == 0) { + parent = encodeInline(QRgb()); + } else { + parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, + totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); + } return allChildrenEqual; } @@ -260,7 +261,7 @@ bool PackedNormalAttribute::merge(void*& parent, void* children[]) const { bool allChildrenEqual = true; for (int i = 1; i < Attribute::MERGE_COUNT; i++) { QRgb value = decodeInline(children[i]); - total += unpackNormal(value); + total += unpackNormal(value) * (float)qAlpha(value); allChildrenEqual &= (firstValue == value); } parent = encodeInline(packNormal(glm::normalize(total))); @@ -316,10 +317,6 @@ bool SharedObjectAttribute::merge(void*& parent, void* children[]) const { return true; } -AttributeValue SharedObjectSetAttribute::split(const AttributeValue& parent) const { - return AttributeValue(); -} - void* SharedObjectAttribute::createFromVariant(const QVariant& value) const { return create(encodeInline(value.value())); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index e0a1c0b3f0..c7d5c057ac 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -121,8 +121,6 @@ public: bool isDefault() const; - AttributeValue split() const; - bool operator==(const AttributeValue& other) const; bool operator==(void* other) const; @@ -208,9 +206,6 @@ public: /// \return whether or not the children and parent values are all equal virtual bool merge(void*& parent, void* children[]) const = 0; - /// Returns the attribute value to pass to children below leaves (either the parent, or the default, or a null value). - virtual AttributeValue split(const AttributeValue& parent) const { return parent; } - /// Mixes the first and the second, returning a new value with the result. virtual void* mix(void* first, void* second, float alpha) const = 0; @@ -346,8 +341,6 @@ public: virtual bool merge(void*& parent, void* children[]) const; - virtual AttributeValue split(const AttributeValue& parent) const; - virtual QWidget* createEditor(QWidget* parent = NULL) const; private: diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c561cbae09..9f46781363 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -953,8 +953,8 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute float lodBase = glm::distance(visitation.visitor.getLOD().position, visitation.info.getCenter()) * visitation.visitor.getLOD().threshold; - visitation.info.isLeaf = (visitation.info.size < lodBase * visitation.visitor.getMinimumLODThresholdMultiplier()) || - visitation.allInputNodesLeaves(); + visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor.getMinimumLODThresholdMultiplier()); + visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); int encodedOrder = visitation.visitor.visit(visitation.info); if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { return false; @@ -991,7 +991,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { MetavoxelNode* child = (node && (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? - child->getAttributeValue(parentValue.getAttribute()) : parentValue.split(); + child->getAttributeValue(parentValue.getAttribute()) : parentValue; } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 3e7c6f1b15..970d529117 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -189,6 +189,7 @@ public: float size; ///< the size of the voxel in all dimensions QVector inputValues; QVector outputValues; + bool isLODLeaf; bool isLeaf; Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } From cba57f20dd078a58296757a350872db135171cb2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Mar 2014 11:38:02 -0700 Subject: [PATCH 23/38] Working on spanner attributes. --- .../metavoxels/src/AttributeRegistry.cpp | 50 ++++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 22 ++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c41420d7c6..548a953857 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -31,8 +31,8 @@ AttributeRegistry::AttributeRegistry() : _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), - _spannerColorAttribute(registerAttribute(new QRgbAttribute("spannerColor"))), - _spannerNormalAttribute(registerAttribute(new PackedNormalAttribute("spannerNormal"))) { + _spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))), + _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))) { // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f; @@ -286,6 +286,52 @@ glm::vec3 unpackNormal(QRgb value) { (char)qBlue(value) * INVERSE_CHAR_SCALE); } +SpannerQRgbAttribute::SpannerQRgbAttribute(const QString& name, QRgb defaultValue) : + QRgbAttribute(name, defaultValue) { +} + +bool SpannerQRgbAttribute::merge(void*& parent, void* children[]) const { + QRgb firstValue = decodeInline(children[0]); + int totalAlpha = qAlpha(firstValue); + int totalRed = qRed(firstValue) * totalAlpha; + int totalGreen = qGreen(firstValue) * totalAlpha; + int totalBlue = qBlue(firstValue) * totalAlpha; + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + QRgb value = decodeInline(children[i]); + int alpha = qAlpha(value); + totalRed += qRed(value) * alpha; + totalGreen += qGreen(value) * alpha; + totalBlue += qBlue(value) * alpha; + totalAlpha += alpha; + allChildrenEqual &= (firstValue == value); + } + if (totalAlpha == 0) { + parent = encodeInline(QRgb()); + } else { + parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, + totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); + } + return allChildrenEqual; +} + +SpannerPackedNormalAttribute::SpannerPackedNormalAttribute(const QString& name, QRgb defaultValue) : + PackedNormalAttribute(name, defaultValue) { +} + +bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[]) const { + QRgb firstValue = decodeInline(children[0]); + glm::vec3 total = unpackNormal(firstValue); + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + QRgb value = decodeInline(children[i]); + total += unpackNormal(value) * (float)qAlpha(value); + allChildrenEqual &= (firstValue == value); + } + parent = encodeInline(packNormal(glm::normalize(total))); + return allChildrenEqual; +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index c7d5c057ac..ce5984d1af 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -299,6 +299,28 @@ QRgb packNormal(const glm::vec3& normal); /// Unpacks a normal from an RGB value. glm::vec3 unpackNormal(QRgb value); +/// RGBA values for voxelized spanners. +class SpannerQRgbAttribute : public QRgbAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE SpannerQRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); + + virtual bool merge(void*& parent, void* children[]) const; +}; + +/// Packed normals for voxelized spanners. +class SpannerPackedNormalAttribute : public PackedNormalAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE SpannerPackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); + + virtual bool merge(void*& parent, void* children[]) const; +}; + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT From 176d8f746e9d19d907b7b257ec7fb760f0d70fd8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Mar 2014 15:19:40 -0700 Subject: [PATCH 24/38] Finally at the point of actually getting spanners to turn into voxels. --- interface/src/MetavoxelSystem.cpp | 2 +- .../metavoxels/src/AttributeRegistry.cpp | 95 ++++++++++++------- libraries/metavoxels/src/AttributeRegistry.h | 23 +++++ libraries/metavoxels/src/MetavoxelData.cpp | 36 ++++--- libraries/metavoxels/src/MetavoxelData.h | 7 +- .../metavoxels/src/MetavoxelMessages.cpp | 45 +++++++-- 6 files changed, 153 insertions(+), 55 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a003f650f5..2da21aae7e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -188,7 +188,7 @@ int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { QRgb color = info.inputValues.at(0).getInlineValue(); QRgb normal = info.inputValues.at(1).getInlineValue(); quint8 alpha = qAlpha(color); - if (info.inputValues.at(4).getAttribute()) { + if (!info.isLODLeaf) { if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha }, diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 548a953857..d9abba9950 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -13,6 +13,8 @@ REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(PackedNormalAttribute) +REGISTER_META_OBJECT(SpannerQRgbAttribute) +REGISTER_META_OBJECT(SpannerPackedNormalAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -35,7 +37,7 @@ AttributeRegistry::AttributeRegistry() : _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))) { // our baseline LOD threshold is for voxels; spanners are a different story - const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f; + const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); } @@ -168,6 +170,10 @@ Attribute::Attribute(const QString& name) : Attribute::~Attribute() { } +MetavoxelNode* Attribute::createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const { + return new MetavoxelNode(value); +} + void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { data.createRoot(state.attribute)->read(state); } @@ -257,7 +263,7 @@ PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultVa bool PackedNormalAttribute::merge(void*& parent, void* children[]) const { QRgb firstValue = decodeInline(children[0]); - glm::vec3 total = unpackNormal(firstValue); + glm::vec3 total = unpackNormal(firstValue) * (float)qAlpha(firstValue); bool allChildrenEqual = true; for (int i = 1; i < Attribute::MERGE_COUNT; i++) { QRgb value = decodeInline(children[i]); @@ -290,46 +296,62 @@ SpannerQRgbAttribute::SpannerQRgbAttribute(const QString& name, QRgb defaultValu QRgbAttribute(name, defaultValue) { } +void SpannerQRgbAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + value = getDefaultValue(); + in.read(&value, 32); +} + +void SpannerQRgbAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + out.write(&value, 32); +} + +MetavoxelNode* SpannerQRgbAttribute::createMetavoxelNode( + const AttributeValue& value, const MetavoxelNode* original) const { + return new MetavoxelNode(value, original); +} + bool SpannerQRgbAttribute::merge(void*& parent, void* children[]) const { - QRgb firstValue = decodeInline(children[0]); - int totalAlpha = qAlpha(firstValue); - int totalRed = qRed(firstValue) * totalAlpha; - int totalGreen = qGreen(firstValue) * totalAlpha; - int totalBlue = qBlue(firstValue) * totalAlpha; - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - totalRed += qRed(value) * alpha; - totalGreen += qGreen(value) * alpha; - totalBlue += qBlue(value) * alpha; - totalAlpha += alpha; - allChildrenEqual &= (firstValue == value); + for (int i = 0; i < MERGE_COUNT; i++) { + if (qAlpha(decodeInline(children[i])) != 0) { + return false; + } } - if (totalAlpha == 0) { - parent = encodeInline(QRgb()); - } else { - parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, - totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); - } - return allChildrenEqual; + return true; } +AttributeValue SpannerQRgbAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + SpannerPackedNormalAttribute::SpannerPackedNormalAttribute(const QString& name, QRgb defaultValue) : PackedNormalAttribute(name, defaultValue) { } +void SpannerPackedNormalAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + value = getDefaultValue(); + in.read(&value, 32); +} + +void SpannerPackedNormalAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + out.write(&value, 32); +} + +MetavoxelNode* SpannerPackedNormalAttribute::createMetavoxelNode( + const AttributeValue& value, const MetavoxelNode* original) const { + return new MetavoxelNode(value, original); +} + bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[]) const { - QRgb firstValue = decodeInline(children[0]); - glm::vec3 total = unpackNormal(firstValue); - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - total += unpackNormal(value) * (float)qAlpha(value); - allChildrenEqual &= (firstValue == value); + for (int i = 0; i < MERGE_COUNT; i++) { + if (qAlpha(decodeInline(children[i])) != 0) { + return false; + } } - parent = encodeInline(packNormal(glm::normalize(total))); - return allChildrenEqual; + return true; +} + +AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); } SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, @@ -386,6 +408,11 @@ void SharedObjectSetAttribute::write(Bitstream& out, void* value, bool isLeaf) c out << decodeInline(value); } +MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode( + const AttributeValue& value, const MetavoxelNode* original) const { + return new MetavoxelNode(value, original); +} + bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { for (int i = 0; i < MERGE_COUNT; i++) { if (!decodeInline(children[i]).isEmpty()) { @@ -395,6 +422,10 @@ bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { return true; } +AttributeValue SharedObjectSetAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + QWidget* SharedObjectSetAttribute::createEditor(QWidget* parent) const { return new SharedObjectEditor(_metaObject, parent); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index ce5984d1af..67388b4cdb 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -191,6 +191,8 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } + virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; + virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); @@ -206,6 +208,9 @@ public: /// \return whether or not the children and parent values are all equal virtual bool merge(void*& parent, void* children[]) const = 0; + /// Given the parent value, returns the value that children should inherit (either the parent value or the default). + virtual AttributeValue inherit(const AttributeValue& parentValue) const { return parentValue; } + /// Mixes the first and the second, returning a new value with the result. virtual void* mix(void* first, void* second, float alpha) const = 0; @@ -307,7 +312,14 @@ public: Q_INVOKABLE SpannerQRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; + virtual bool merge(void*& parent, void* children[]) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; /// Packed normals for voxelized spanners. @@ -318,7 +330,14 @@ public: Q_INVOKABLE SpannerPackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; + virtual bool merge(void*& parent, void* children[]) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). @@ -361,8 +380,12 @@ public: virtual void read(Bitstream& in, void*& value, bool isLeaf) const; virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; + virtual bool merge(void*& parent, void* children[]) const; + virtual AttributeValue inherit(const AttributeValue& parentValue) const; + virtual QWidget* createEditor(QWidget* parent = NULL) const; private: diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 0a1f7ba365..ed1de6b4ad 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -485,14 +485,26 @@ void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) { minimum = getNextMinimum(lastMinimum, size, index); } -MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceCount(1) { +MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren) : + _referenceCount(1) { + _attributeValue = attributeValue.copy(); - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = NULL; + if (copyChildren) { + for (int i = 0; i < CHILD_COUNT; i++) { + if ((_children[i] = copyChildren->_children[i])) { + _children[i]->incrementReferenceCount(); + } + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i] = NULL; + } } } -MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy) : _referenceCount(1) { +MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy) : + _referenceCount(1) { + _attributeValue = attribute->create(copy->_attributeValue); for (int i = 0; i < CHILD_COUNT; i++) { if ((_children[i] = copy->_children[i])) { @@ -969,7 +981,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // "set" to same value; disregard value = AttributeValue(); } else { - node = new MetavoxelNode(value); + node = value.getAttribute()->createMetavoxelNode(value, node); } } if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { @@ -991,7 +1003,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { MetavoxelNode* child = (node && (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? - child->getAttributeValue(parentValue.getAttribute()) : parentValue; + child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); @@ -1019,7 +1031,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { node = new MetavoxelNode(value.getAttribute(), node); } else { // create leaf with inherited value - node = new MetavoxelNode(visitation.getInheritedOutputValue(j)); + node = new MetavoxelNode(value.getAttribute()->inherit(visitation.getInheritedOutputValue(j))); } } MetavoxelNode* node = visitation.outputNodes.at(j); @@ -1028,7 +1040,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { child->decrementReferenceCount(value.getAttribute()); } else { // it's a leaf; we need to split it up - AttributeValue nodeValue = node->getAttributeValue(value.getAttribute()); + AttributeValue nodeValue = value.getAttribute()->inherit(node->getAttributeValue(value.getAttribute())); for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) { node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); } @@ -1234,7 +1246,7 @@ bool Spanner::getAttributeValues(MetavoxelInfo& info) const { return false; } -bool Spanner::blendAttributeValues(MetavoxelInfo& info) const { +bool Spanner::blendAttributeValues(MetavoxelInfo& info, bool force) const { return false; } @@ -1369,10 +1381,10 @@ bool Sphere::getAttributeValues(MetavoxelInfo& info) const { return true; } -bool Sphere::blendAttributeValues(MetavoxelInfo& info) const { +bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const { // bounds check Box bounds = info.getBounds(); - if (!getBounds().intersects(bounds)) { + if (!(force || getBounds().intersects(bounds))) { return false; } // count the points inside the sphere @@ -1388,7 +1400,7 @@ bool Sphere::blendAttributeValues(MetavoxelInfo& info) const { info.outputValues[1] = getNormal(info); return false; } - if (info.size <= getVoxelizationGranularity()) { + if (force || info.size <= getVoxelizationGranularity()) { // best guess if (pointsWithin > 0) { int oldAlpha = qAlpha(info.inputValues.at(0).getInlineValue()); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 970d529117..85ff2d0850 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -132,7 +132,7 @@ public: static const int CHILD_COUNT = 8; - MetavoxelNode(const AttributeValue& attributeValue); + MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren = NULL); MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy); void setAttributeValue(const AttributeValue& attributeValue); @@ -439,8 +439,9 @@ public: virtual bool getAttributeValues(MetavoxelInfo& info) const; /// Blends the attribute values associated with this spanner into the supplied info. + /// \param force if true, blend even if we would normally subdivide /// \return true to recurse, false to stop - virtual bool blendAttributeValues(MetavoxelInfo& info) const; + virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. @@ -536,7 +537,7 @@ public: virtual const QVector& getAttributes() const; virtual const QVector& getVoxelizedAttributes() const; virtual bool getAttributeValues(MetavoxelInfo& info) const; - virtual bool blendAttributeValues(MetavoxelInfo& info) const; + virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; signals: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index b600ef87bf..46924f8af8 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -104,32 +104,42 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } -class SetSpannerEditVisitor : public MetavoxelVisitor { +class InsertSpannerEditVisitor : public MetavoxelVisitor { public: - SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner); + InsertSpannerEditVisitor(const QVector& attributes, Spanner* spanner); virtual int visit(MetavoxelInfo& info); private: Spanner* _spanner; + float _longestSide; }; -SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : +InsertSpannerEditVisitor::InsertSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : MetavoxelVisitor(attributes, attributes), - _spanner(spanner) { + _spanner(spanner), + _longestSide(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f / + AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) { } -int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { - return _spanner->blendAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; +int InsertSpannerEditVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_spanner->getBounds())) { + return STOP_RECURSION; + } + if (info.size > _longestSide) { + return DEFAULT_ORDER; + } + _spanner->blendAttributeValues(info, true); + return STOP_RECURSION; } void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { data.insert(attribute, this->spanner); Spanner* spanner = static_cast(this->spanner.data()); - SetSpannerEditVisitor visitor(spanner->getVoxelizedAttributes(), spanner); + InsertSpannerEditVisitor visitor(spanner->getVoxelizedAttributes(), spanner); data.guide(visitor); } @@ -159,6 +169,27 @@ SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : spanner(spanner) { } +class SetSpannerEditVisitor : public MetavoxelVisitor { +public: + + SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner); + + virtual int visit(MetavoxelInfo& info); + +private: + + Spanner* _spanner; +}; + +SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : + MetavoxelVisitor(attributes, attributes), + _spanner(spanner) { +} + +int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { + return _spanner->blendAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; +} + void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { Spanner* spanner = static_cast(this->spanner.data()); From 7580951b9280e5d80188bb7ca7ececcbbf3845be Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Mar 2014 20:19:28 -0700 Subject: [PATCH 25/38] More progress on voxelizing spanners. --- .../metavoxels/src/AttributeRegistry.cpp | 69 +++++++++++++++---- libraries/metavoxels/src/AttributeRegistry.h | 16 ++--- libraries/metavoxels/src/MetavoxelData.cpp | 65 +++++++++-------- libraries/metavoxels/src/MetavoxelData.h | 9 +-- .../metavoxels/src/MetavoxelMessages.cpp | 54 +++++++++++---- 5 files changed, 144 insertions(+), 69 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index d9abba9950..ec0cf06dc6 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -202,7 +202,7 @@ QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : InlineAttribute(name, defaultValue) { } -bool QRgbAttribute::merge(void*& parent, void* children[]) const { +bool QRgbAttribute::merge(void*& parent, void* children[], bool postRead) const { QRgb firstValue = decodeInline(children[0]); int totalAlpha = qAlpha(firstValue); int totalRed = qRed(firstValue) * totalAlpha; @@ -261,7 +261,7 @@ PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultVa QRgbAttribute(name, defaultValue) { } -bool PackedNormalAttribute::merge(void*& parent, void* children[]) const { +bool PackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const { QRgb firstValue = decodeInline(children[0]); glm::vec3 total = unpackNormal(firstValue) * (float)qAlpha(firstValue); bool allChildrenEqual = true; @@ -270,7 +270,8 @@ bool PackedNormalAttribute::merge(void*& parent, void* children[]) const { total += unpackNormal(value) * (float)qAlpha(value); allChildrenEqual &= (firstValue == value); } - parent = encodeInline(packNormal(glm::normalize(total))); + float length = glm::length(total); + parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length)); return allChildrenEqual; } @@ -310,13 +311,37 @@ MetavoxelNode* SpannerQRgbAttribute::createMetavoxelNode( return new MetavoxelNode(value, original); } -bool SpannerQRgbAttribute::merge(void*& parent, void* children[]) const { - for (int i = 0; i < MERGE_COUNT; i++) { - if (qAlpha(decodeInline(children[i])) != 0) { - return false; +bool SpannerQRgbAttribute::merge(void*& parent, void* children[], bool postRead) const { + if (postRead) { + for (int i = 0; i < MERGE_COUNT; i++) { + if (qAlpha(decodeInline(children[i])) != 0) { + return false; + } } + return true; } - return true; + QRgb parentValue = decodeInline(parent); + int totalAlpha = qAlpha(parentValue) * Attribute::MERGE_COUNT; + int totalRed = qRed(parentValue) * totalAlpha; + int totalGreen = qGreen(parentValue) * totalAlpha; + int totalBlue = qBlue(parentValue) * totalAlpha; + bool allChildrenTransparent = true; + for (int i = 0; i < Attribute::MERGE_COUNT; i++) { + QRgb value = decodeInline(children[i]); + int alpha = qAlpha(value); + totalRed += qRed(value) * alpha; + totalGreen += qGreen(value) * alpha; + totalBlue += qBlue(value) * alpha; + totalAlpha += alpha; + allChildrenTransparent &= (alpha == 0); + } + if (totalAlpha == 0) { + parent = encodeInline(QRgb()); + } else { + parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, + totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); + } + return allChildrenTransparent; } AttributeValue SpannerQRgbAttribute::inherit(const AttributeValue& parentValue) const { @@ -341,13 +366,27 @@ MetavoxelNode* SpannerPackedNormalAttribute::createMetavoxelNode( return new MetavoxelNode(value, original); } -bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[]) const { - for (int i = 0; i < MERGE_COUNT; i++) { - if (qAlpha(decodeInline(children[i])) != 0) { - return false; +bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const { + if (postRead) { + for (int i = 0; i < MERGE_COUNT; i++) { + if (qAlpha(decodeInline(children[i])) != 0) { + return false; + } } + return true; } - return true; + QRgb parentValue = decodeInline(parent); + glm::vec3 total = unpackNormal(parentValue) * (float)(qAlpha(parentValue) * Attribute::MERGE_COUNT); + bool allChildrenTransparent = true; + for (int i = 0; i < Attribute::MERGE_COUNT; i++) { + QRgb value = decodeInline(children[i]); + int alpha = qAlpha(value); + total += unpackNormal(value) * (float)alpha; + allChildrenTransparent &= (alpha == 0); + } + float length = glm::length(total); + parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length)); + return allChildrenTransparent; } AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& parentValue) const { @@ -373,7 +412,7 @@ void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) cons } } -bool SharedObjectAttribute::merge(void*& parent, void* children[]) const { +bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const { SharedObjectPointer firstChild = decodeInline(children[0]); for (int i = 1; i < MERGE_COUNT; i++) { if (firstChild != decodeInline(children[i])) { @@ -413,7 +452,7 @@ MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode( return new MetavoxelNode(value, original); } -bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { +bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const { for (int i = 0; i < MERGE_COUNT; i++) { if (!decodeInline(children[i]).isEmpty()) { return false; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 67388b4cdb..28a110467c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -115,7 +115,6 @@ public: template void setInlineValue(T value) { _value = encodeInline(value); } template T getInlineValue() const { return decodeInline(_value); } - template T getSafeInlineValue() const { return _attribute ? decodeInline(_value) : T(); } void* copy() const; @@ -205,8 +204,9 @@ public: virtual bool equal(void* first, void* second) const = 0; /// Merges the value of a parent and its children. + /// \param postRead whether or not the merge is happening after a read /// \return whether or not the children and parent values are all equal - virtual bool merge(void*& parent, void* children[]) const = 0; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const = 0; /// Given the parent value, returns the value that children should inherit (either the parent value or the default). virtual AttributeValue inherit(const AttributeValue& parentValue) const { return parentValue; } @@ -274,7 +274,7 @@ public: Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - virtual bool merge(void*& parent, void* children[]) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual void* mix(void* first, void* second, float alpha) const; @@ -293,7 +293,7 @@ public: Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - virtual bool merge(void*& parent, void* children[]) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual void* mix(void* first, void* second, float alpha) const; }; @@ -317,7 +317,7 @@ public: virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - virtual bool merge(void*& parent, void* children[]) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; @@ -335,7 +335,7 @@ public: virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - virtual bool merge(void*& parent, void* children[]) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; @@ -354,7 +354,7 @@ public: virtual void read(Bitstream& in, void*& value, bool isLeaf) const; virtual void write(Bitstream& out, void* value, bool isLeaf) const; - virtual bool merge(void*& parent, void* children[]) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual void* createFromVariant(const QVariant& value) const; @@ -382,7 +382,7 @@ public: virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - virtual bool merge(void*& parent, void* children[]) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual AttributeValue inherit(const AttributeValue& parentValue) const; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index ed1de6b4ad..aae4d7761f 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -85,7 +85,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { const QVector& inputs = visitor.getInputs(); const QVector& outputs = visitor.getOutputs(); MetavoxelVisitation firstVisitation = { NULL, visitor, QVector(inputs.size() + 1), - QVector(outputs.size()), { getMinimum(), _size, + QVector(outputs.size()), { NULL, getMinimum(), _size, QVector(inputs.size() + 1), QVector(outputs.size()) } }; for (int i = 0; i < inputs.size(); i++) { MetavoxelNode* node = _roots.value(inputs.at(i)); @@ -169,7 +169,7 @@ template int SpannerUpdateVisitor::visit(MetavoxelIn if (info.size > _longestSide) { return DEFAULT_ORDER; } - SharedObjectSet set = info.inputValues.at(0).getSafeInlineValue(); + SharedObjectSet set = info.inputValues.at(0).getInlineValue(); F(set, _object); info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); return STOP_RECURSION; @@ -523,14 +523,17 @@ AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribut return AttributeValue(attribute, _attributeValue); } -void MetavoxelNode::mergeChildren(const AttributePointer& attribute) { +void MetavoxelNode::mergeChildren(const AttributePointer& attribute, bool postRead) { + if (isLeaf()) { + return; + } void* childValues[CHILD_COUNT]; bool allLeaves = true; for (int i = 0; i < CHILD_COUNT; i++) { childValues[i] = _children[i]->_attributeValue; allLeaves &= _children[i]->isLeaf(); } - if (attribute->merge(_attributeValue, childValues) && allLeaves) { + if (attribute->merge(_attributeValue, childValues, postRead) && allLeaves) { clearChildren(attribute); } } @@ -562,7 +565,7 @@ void MetavoxelNode::read(MetavoxelStreamState& state) { _children[i] = new MetavoxelNode(state.attribute); _children[i]->read(nextState); } - mergeChildren(state.attribute); + mergeChildren(state.attribute, true); } } @@ -620,7 +623,7 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta } } } - mergeChildren(state.attribute); + mergeChildren(state.attribute, true); } } @@ -887,7 +890,7 @@ void SpannerVisitor::prepare() { int SpannerVisitor::visit(MetavoxelInfo& info) { for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { - foreach (const SharedObjectPointer& object, info.inputValues.at(i).getSafeInlineValue()) { + foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { if (!visit(spanner)) { @@ -939,7 +942,7 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { - foreach (const SharedObjectPointer& object, info.inputValues.at(i).getSafeInlineValue()) { + foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { SpannerDistance spannerDistance = { spanner }; @@ -989,7 +992,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } MetavoxelVisitation nextVisitation = { &visitation, visitation.visitor, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), - { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), + { &visitation.info, glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { // the encoded order tells us the child indices for each iteration @@ -1107,7 +1110,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin QScriptValue infoValue = context->argument(0); QScriptValue minimum = infoValue.property(guide->_minimumHandle); MetavoxelInfo info = { - glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), + NULL, glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), (float)infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues, guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; @@ -1242,7 +1245,7 @@ const QVector& Spanner::getVoxelizedAttributes() const { return emptyVector; } -bool Spanner::getAttributeValues(MetavoxelInfo& info) const { +bool Spanner::getAttributeValues(MetavoxelInfo& info, bool force) const { return false; } @@ -1350,10 +1353,10 @@ const QVector& Sphere::getVoxelizedAttributes() const { return attributes; } -bool Sphere::getAttributeValues(MetavoxelInfo& info) const { +bool Sphere::getAttributeValues(MetavoxelInfo& info, bool force) const { // bounds check Box bounds = info.getBounds(); - if (!getBounds().intersects(bounds)) { + if (!(force || getBounds().intersects(bounds))) { return false; } // count the points inside the sphere @@ -1366,15 +1369,16 @@ bool Sphere::getAttributeValues(MetavoxelInfo& info) const { if (pointsWithin == Box::VERTEX_COUNT) { // entirely contained info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); - getNormal(info); + info.outputValues[1] = getNormal(info, _color.alpha()); return false; } - if (info.size <= getVoxelizationGranularity()) { + if (force || info.size <= getVoxelizationGranularity()) { // best guess if (pointsWithin > 0) { + int alpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(qRgba( - _color.red(), _color.green(), _color.blue(), _color.alpha() * pointsWithin / Box::VERTEX_COUNT))); - getNormal(info); + _color.red(), _color.green(), _color.blue(), alpha))); + info.outputValues[1] = getNormal(info, alpha); } return false; } @@ -1397,19 +1401,23 @@ bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const { if (pointsWithin == Box::VERTEX_COUNT) { // entirely contained info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); - info.outputValues[1] = getNormal(info); + info.outputValues[1] = getNormal(info, _color.alpha()); return false; } if (force || info.size <= getVoxelizationGranularity()) { // best guess if (pointsWithin > 0) { - int oldAlpha = qAlpha(info.inputValues.at(0).getInlineValue()); + const AttributeValue& oldColor = info.outputValues.at(0).getAttribute() ? + info.outputValues.at(0) : info.inputValues.at(0); + const AttributeValue& oldNormal = info.outputValues.at(1).getAttribute() ? + info.outputValues.at(1) : info.inputValues.at(1); + int oldAlpha = qAlpha(oldColor.getInlineValue()); int newAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; float combinedAlpha = (float)newAlpha / (oldAlpha + newAlpha); - info.outputValues[0].mix(info.inputValues.at(0), AttributeValue(getAttributes().at(0), - encodeInline(qRgba(_color.red(), _color.green(), _color.blue(), - _color.alpha() * pointsWithin / Box::VERTEX_COUNT))), combinedAlpha); - info.outputValues[1].mix(info.inputValues.at(1), getNormal(info), combinedAlpha); + int baseAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; + info.outputValues[0].mix(oldColor, AttributeValue(getAttributes().at(0), + encodeInline(qRgba(_color.red(), _color.green(), _color.blue(), baseAlpha))), combinedAlpha); + info.outputValues[1].mix(oldNormal, getNormal(info, baseAlpha), combinedAlpha); } return false; } @@ -1429,20 +1437,19 @@ void Sphere::updateBounds() { setBounds(Box(getTranslation() - extent, getTranslation() + extent)); } -AttributeValue Sphere::getNormal(MetavoxelInfo& info) const { +AttributeValue Sphere::getNormal(MetavoxelInfo& info, int alpha) const { glm::vec3 normal = info.getCenter() - getTranslation(); float length = glm::length(normal); QRgb color; - if (length > EPSILON) { + if (alpha != 0 && length > EPSILON) { const float NORMAL_SCALE = 127.0f; float scale = NORMAL_SCALE / length; const int BYTE_MASK = 0xFF; - color = qRgb((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK, - (int)(normal.z * scale) & BYTE_MASK); + color = qRgba((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK, + (int)(normal.z * scale) & BYTE_MASK, alpha); } else { - const QRgb DEFAULT_NORMAL = 0x007F00; - color = DEFAULT_NORMAL; + color = QRgb(); } return AttributeValue(getAttributes().at(1), encodeInline(color)); } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 85ff2d0850..fd7ce437cc 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -140,7 +140,7 @@ public: AttributeValue getAttributeValue(const AttributePointer& attribute) const; void* getAttributeValue() const { return _attributeValue; } - void mergeChildren(const AttributePointer& attribute); + void mergeChildren(const AttributePointer& attribute, bool postRead = false); MetavoxelNode* getChild(int index) const { return _children[index]; } void setChild(int index, MetavoxelNode* child) { _children[index] = child; } @@ -185,6 +185,7 @@ private: class MetavoxelInfo { public: + MetavoxelInfo* parentInfo; glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel float size; ///< the size of the voxel in all dimensions QVector inputValues; @@ -436,7 +437,7 @@ public: /// Sets the attribute values associated with this spanner in the supplied info. /// \return true to recurse, false to stop - virtual bool getAttributeValues(MetavoxelInfo& info) const; + virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; /// Blends the attribute values associated with this spanner into the supplied info. /// \param force if true, blend even if we would normally subdivide @@ -536,7 +537,7 @@ public: virtual const QVector& getAttributes() const; virtual const QVector& getVoxelizedAttributes() const; - virtual bool getAttributeValues(MetavoxelInfo& info) const; + virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; @@ -554,7 +555,7 @@ private slots: private: - AttributeValue getNormal(MetavoxelInfo& info) const; + AttributeValue getNormal(MetavoxelInfo& info, int alpha) const; QColor _color; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 46924f8af8..59b643a249 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -104,42 +104,64 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } -class InsertSpannerEditVisitor : public MetavoxelVisitor { +class UpdateSpannerVisitor : public MetavoxelVisitor { public: - InsertSpannerEditVisitor(const QVector& attributes, Spanner* spanner); + UpdateSpannerVisitor(const QVector& attributes, Spanner* spanner); virtual int visit(MetavoxelInfo& info); private: Spanner* _spanner; - float _longestSide; + float _voxelizationSize; + int _steps; }; -InsertSpannerEditVisitor::InsertSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(attributes, attributes), +UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector& attributes, Spanner* spanner) : + MetavoxelVisitor(QVector() << attributes << AttributeRegistry::getInstance()->getSpannersAttribute(), + attributes), _spanner(spanner), - _longestSide(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f / - AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) { + _voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f / + AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()), + _steps(roundf(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) / + logf(2.0f) - 2.0f)) { } -int InsertSpannerEditVisitor::visit(MetavoxelInfo& info) { +int UpdateSpannerVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_spanner->getBounds())) { return STOP_RECURSION; } - if (info.size > _longestSide) { - return DEFAULT_ORDER; + MetavoxelInfo* parentInfo = info.parentInfo; + for (int i = 0; i < _steps && parentInfo; i++) { + parentInfo = parentInfo->parentInfo; } - _spanner->blendAttributeValues(info, true); - return STOP_RECURSION; + if (!parentInfo) { + for (int i = 0; i < _outputs.size(); i++) { + info.outputValues[i] = AttributeValue(_outputs.at(i)); + } + return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; + } + SharedObjectSet objects = parentInfo->inputValues.at(_outputs.size()).getInlineValue(); + if (objects.isEmpty()) { + for (int i = 0; i < _outputs.size(); i++) { + info.outputValues[i] = AttributeValue(_outputs.at(i)); + } + return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; + } + SharedObjectSet::const_iterator it = objects.constBegin(); + static_cast(it->data())->getAttributeValues(info, true); + for (it++; it != objects.constEnd(); it++) { + static_cast(it->data())->blendAttributeValues(info, true); + } + return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; } void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { data.insert(attribute, this->spanner); Spanner* spanner = static_cast(this->spanner.data()); - InsertSpannerEditVisitor visitor(spanner->getVoxelizedAttributes(), spanner); + UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner); data.guide(visitor); } @@ -154,7 +176,13 @@ void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o qDebug() << "Missing object to remove" << id; return; } + // keep a strong reference to the object + SharedObjectPointer sharedPointer = object; data.remove(attribute, object); + + Spanner* spanner = static_cast(object); + UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner); + data.guide(visitor); } ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : From 40b1d2f2849fe5095d1d53fcc2a40ec91bc656d6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sat, 29 Mar 2014 22:39:49 -0700 Subject: [PATCH 26/38] Fix for voxelization on removal. --- .../metavoxels/src/MetavoxelMessages.cpp | 21 ++++++------------- libraries/metavoxels/src/MetavoxelUtil.cpp | 5 +++++ libraries/metavoxels/src/MetavoxelUtil.h | 2 ++ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 59b643a249..1f6c356a38 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -136,23 +136,14 @@ int UpdateSpannerVisitor::visit(MetavoxelInfo& info) { for (int i = 0; i < _steps && parentInfo; i++) { parentInfo = parentInfo->parentInfo; } - if (!parentInfo) { - for (int i = 0; i < _outputs.size(); i++) { - info.outputValues[i] = AttributeValue(_outputs.at(i)); - } - return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; + for (int i = 0; i < _outputs.size(); i++) { + info.outputValues[i] = AttributeValue(_outputs.at(i)); } - SharedObjectSet objects = parentInfo->inputValues.at(_outputs.size()).getInlineValue(); - if (objects.isEmpty()) { - for (int i = 0; i < _outputs.size(); i++) { - info.outputValues[i] = AttributeValue(_outputs.at(i)); + if (parentInfo) { + foreach (const SharedObjectPointer& object, + parentInfo->inputValues.at(_outputs.size()).getInlineValue()) { + static_cast(object.data())->blendAttributeValues(info, true); } - return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; - } - SharedObjectSet::const_iterator it = objects.constBegin(); - static_cast(it->data())->getAttributeValues(info, true); - for (it++; it != objects.constEnd(); it++) { - static_cast(it->data())->blendAttributeValues(info, true); } return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; } diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 7993b3bcb6..7995809f1c 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -299,6 +299,11 @@ Box operator*(const glm::mat4& matrix, const Box& box) { return newBox; } +QDebug& operator<<(QDebug& out, const Box& box) { + return out << '(' << box.minimum.x << box.minimum.y << box.minimum.z << ") (" << + box.maximum.x << box.maximum.y << box.maximum.z << ')'; +} + QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(QMargins()); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 3f450212b1..9972981bc7 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -60,6 +60,8 @@ DECLARE_STREAMABLE_METATYPE(Box) Box operator*(const glm::mat4& matrix, const Box& box); +QDebug& operator<<(QDebug& out, const Box& box); + /// Editor for meta-object values. class QMetaObjectEditor : public QWidget { Q_OBJECT From 10588cff9f92efb10fb5473800adaadc0754a03d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 30 Mar 2014 15:51:39 -0700 Subject: [PATCH 27/38] "Clear spanners" clears the voxelized attributes, too. --- .../metavoxels/src/MetavoxelMessages.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 1f6c356a38..ac4f8fc7f6 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -180,8 +180,40 @@ ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : attribute(attribute) { } +class GetSpannerAttributesVisitor : public SpannerVisitor { +public: + + GetSpannerAttributesVisitor(const AttributePointer& attribute); + + const QSet& getAttributes() const { return _attributes; } + + virtual bool visit(Spanner* spanner); + +protected: + + QSet _attributes; +}; + +GetSpannerAttributesVisitor::GetSpannerAttributesVisitor(const AttributePointer& attribute) : + SpannerVisitor(QVector() << attribute) { +} + +bool GetSpannerAttributesVisitor::visit(Spanner* spanner) { + foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) { + _attributes.insert(attribute); + } + return true; +} + void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // find all the spanner attributes + GetSpannerAttributesVisitor visitor(attribute); + data.guide(visitor); + data.clear(attribute); + foreach (const AttributePointer& attribute, visitor.getAttributes()) { + data.clear(attribute); + } } SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : From 005ae79928fe74c6febd0777dbdaaed7c636e73a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 31 Mar 2014 18:21:28 -0700 Subject: [PATCH 28/38] Various fixes and what-not, got the basic spanner clipping working. --- interface/src/MetavoxelSystem.cpp | 63 ++++++- interface/src/MetavoxelSystem.h | 35 +++- interface/src/ui/MetavoxelEditor.cpp | 2 +- .../metavoxels/src/AttributeRegistry.cpp | 8 +- libraries/metavoxels/src/AttributeRegistry.h | 37 ++++ libraries/metavoxels/src/Bitstream.cpp | 6 +- libraries/metavoxels/src/MetavoxelData.cpp | 166 +++++++++++++++--- libraries/metavoxels/src/MetavoxelData.h | 36 +++- .../metavoxels/src/MetavoxelMessages.cpp | 75 ++++++-- 9 files changed, 363 insertions(+), 65 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 2da21aae7e..042c9329f2 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -167,14 +167,14 @@ void MetavoxelSystem::maybeAttachClient(const SharedNodePointer& node) { MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector() << AttributeRegistry::getInstance()->getColorAttribute() << + QVector(), QVector() << AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute() << AttributeRegistry::getInstance()->getSpannerColorAttribute() << AttributeRegistry::getInstance()->getSpannerNormalAttribute()), _points(points) { } -bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) { +bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { spanner->getRenderer()->simulate(_deltaTime); return true; } @@ -223,11 +223,12 @@ int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { } MetavoxelSystem::RenderVisitor::RenderVisitor() : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()) { + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector() << AttributeRegistry::getInstance()->getSpannerMaskAttribute()) { } -bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { - spanner->getRenderer()->render(1.0f); +bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { + spanner->getRenderer()->render(1.0f, clipMinimum, clipSize); return true; } @@ -333,10 +334,55 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } +static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { + GLdouble coefficients[] = { x, y, z, w }; + glClipPlane(plane, coefficients); + glEnable(plane); +} + +void ClippedRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) { + if (clipSize == 0.0f) { + renderUnclipped(alpha); + return; + } + enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize); + enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); + enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipSize); + enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); + enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize); + enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z); + + renderUnclipped(alpha); + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + glDisable(GL_CLIP_PLANE4); + glDisable(GL_CLIP_PLANE5); +} + SphereRenderer::SphereRenderer() { } -void SphereRenderer::render(float alpha) { +void SphereRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) { + if (clipSize == 0.0f) { + renderUnclipped(alpha); + return; + } + // slight performance optimization: don't render if clip bounds are entirely within sphere + Sphere* sphere = static_cast(parent()); + Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); + for (int i = 0; i < Box::VERTEX_COUNT; i++) { + const float CLIP_PROPORTION = 0.95f; + if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) { + ClippedRenderer::render(alpha, clipMinimum, clipSize); + return; + } + } +} + +void SphereRenderer::renderUnclipped(float alpha) { Sphere* sphere = static_cast(parent()); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha); @@ -384,11 +430,12 @@ void StaticModelRenderer::simulate(float deltaTime) { _model->simulate(deltaTime); } -void StaticModelRenderer::render(float alpha) { +void StaticModelRenderer::renderUnclipped(float alpha) { _model->render(alpha); } -bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return _model->findRayIntersection(origin, direction, distance); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index ea3f4ed18f..58f40dad37 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -61,7 +61,7 @@ private: SimulateVisitor(QVector& points); void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; } void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); } - virtual bool visit(Spanner* spanner); + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); virtual int visit(MetavoxelInfo& info); private: @@ -73,7 +73,7 @@ private: class RenderVisitor : public SpannerVisitor { public: RenderVisitor(); - virtual bool visit(Spanner* spanner); + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); }; static ProgramObject _program; @@ -141,19 +141,36 @@ private: QList _receiveRecords; }; +/// Base class for spanner renderers; provides clipping. +class ClippedRenderer : public SpannerRenderer { + Q_OBJECT + +public: + + virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize); + +protected: + + virtual void renderUnclipped(float alpha) = 0; +}; + /// Renders spheres. -class SphereRenderer : public SpannerRenderer { +class SphereRenderer : public ClippedRenderer { Q_OBJECT public: Q_INVOKABLE SphereRenderer(); - virtual void render(float alpha); + virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize); + +protected: + + virtual void renderUnclipped(float alpha); }; /// Renders static models. -class StaticModelRenderer : public SpannerRenderer { +class StaticModelRenderer : public ClippedRenderer { Q_OBJECT public: @@ -162,8 +179,12 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(float alpha); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; + +protected: + + virtual void renderUnclipped(float alpha); private slots: diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 7a70cb9a4c..19f5902336 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -565,7 +565,7 @@ void PlaceSpannerTool::render() { } Spanner* spanner = static_cast(_editor->getValue().value().data()); const float SPANNER_ALPHA = 0.25f; - spanner->getRenderer()->render(SPANNER_ALPHA); + spanner->getRenderer()->render(SPANNER_ALPHA, glm::vec3(), 0.0f); } bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index ec0cf06dc6..fc448613f9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -11,6 +11,7 @@ #include "AttributeRegistry.h" #include "MetavoxelData.h" +REGISTER_META_OBJECT(FloatAttribute) REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(SpannerQRgbAttribute) @@ -34,7 +35,8 @@ AttributeRegistry::AttributeRegistry() : _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), _spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))), - _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))) { + _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))), + _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))) { // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -198,6 +200,10 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt root.writeSubdivision(state); } +FloatAttribute::FloatAttribute(const QString& name, float defaultValue) : + SimpleInlineAttribute(name, defaultValue) { +} + QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : InlineAttribute(name, defaultValue) { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 28a110467c..f7d8d955a5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -80,6 +80,9 @@ public: /// Returns a reference to the standard packed normal "spannerNormal" attribute. const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; } + /// Returns a reference to the standard "spannerMask" attribute. + const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -91,6 +94,7 @@ private: AttributePointer _normalAttribute; AttributePointer _spannerColorAttribute; AttributePointer _spannerNormalAttribute; + AttributePointer _spannerMaskAttribute; }; /// Converts a value to a void pointer. @@ -265,6 +269,39 @@ template inline void InlineAttribute::write(Bitstrea } } +/// Provides averaging using the +=, ==, and / operators. +template class SimpleInlineAttribute : public InlineAttribute { +public: + + SimpleInlineAttribute(const QString& name, const T& defaultValue = T()) : InlineAttribute(name, defaultValue) { } + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + +template inline bool SimpleInlineAttribute::merge( + void*& parent, void* children[], bool postRead) const { + T firstValue = decodeInline(children[0]); + T totalValue = firstValue; + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + T value = decodeInline(children[i]); + totalValue += value; + allChildrenEqual &= (firstValue == value); + } + parent = encodeInline(totalValue / Attribute::MERGE_COUNT); + return allChildrenEqual; +} + +/// Simple float attribute. +class FloatAttribute : public SimpleInlineAttribute { + Q_OBJECT + Q_PROPERTY(float defaultValue MEMBER _defaultValue) + +public: + + Q_INVOKABLE FloatAttribute(const QString& name = QString(), float defaultValue = 0.0f); +}; + /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 059a457e5e..077e6c1c69 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -223,12 +223,12 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { if (!it.value()) { continue; } - QPointer& reference = _sharedObjectReferences[it.value()->getID()]; + QPointer& reference = _sharedObjectReferences[it.value()->getRemoteID()]; if (reference) { _sharedObjectStreamer.removePersistentValue(reference.data()); } reference = it.value(); - _weakSharedObjectHash.remove(it.value()->getID()); + _weakSharedObjectHash.remove(it.value()->getRemoteID()); } } @@ -863,7 +863,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } } else { QObject* rawObject; - if (reference) { + if (reference) { readRawDelta(rawObject, (QObject*)reference.data()); } else { *this >> rawObject; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index aae4d7761f..e548de46c7 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -212,6 +212,67 @@ void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds, guide(visitor); } +void MetavoxelData::replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, + const SharedObjectPointer& newObject) { + Spanner* spanner = static_cast(oldObject.data()); + replace(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), oldObject, newObject); +} + +class SpannerReplaceVisitor : public MetavoxelVisitor { +public: + + SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); + + virtual int visit(MetavoxelInfo& info); + +private: + + const AttributePointer& _attribute; + const Box& _bounds; + float _longestSide; + const SharedObjectPointer& _oldObject; + const SharedObjectPointer& _newObject; +}; + +SpannerReplaceVisitor::SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, + const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) : + MetavoxelVisitor(QVector() << attribute, QVector() << attribute), + _attribute(attribute), + _bounds(bounds), + _longestSide(qMax(bounds.getLongestSide(), granularity)), + _oldObject(oldObject), + _newObject(newObject) { +} + +int SpannerReplaceVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + if (info.size > _longestSide) { + return DEFAULT_ORDER; + } + SharedObjectSet set = info.inputValues.at(0).getInlineValue(); + if (set.remove(_oldObject)) { + set.insert(_newObject); + } + info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); + return STOP_RECURSION; +} + +void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity, + const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) { + Spanner* newSpanner = static_cast(newObject.data()); + if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) { + // if the bounds have changed, we must remove and reinsert + remove(attribute, bounds, granularity, oldObject); + insert(attribute, newSpanner->getBounds(), newSpanner->getPlacementGranularity(), newObject); + return; + } + SpannerReplaceVisitor visitor(attribute, bounds, granularity, oldObject, newObject); + guide(visitor); +} + void MetavoxelData::clear(const AttributePointer& attribute) { MetavoxelNode* node = _roots.take(attribute); if (node) { @@ -239,7 +300,7 @@ private: FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) : RaySpannerIntersectionVisitor(origin, direction, QVector() << attribute, - QVector(), QVector(), lod), + QVector(), QVector(), QVector(), lod), _spanner(NULL) { } @@ -878,10 +939,11 @@ void MetavoxelVisitor::prepare() { // nothing by default } -SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod) : - MetavoxelVisitor(inputs + spannerInputs, outputs, lod), - _spannerInputCount(spannerInputs.size()) { +SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& spannerMasks, + const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : + MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod), + _spannerInputCount(spannerInputs.size()), + _spannerMaskCount(spannerMasks.size()) { } void SpannerVisitor::prepare() { @@ -889,17 +951,34 @@ void SpannerVisitor::prepare() { } int SpannerVisitor::visit(MetavoxelInfo& info) { - for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { + for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (spanner->testAndSetVisited()) { - if (!visit(spanner)) { - return SHORT_CIRCUIT; - } + if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited() && + !visit(spanner, glm::vec3(), 0.0f)) { + return SHORT_CIRCUIT; } } } - return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { + float maskValue = info.inputValues.at(i).getInlineValue(); + if (maskValue < 0.5f) { + const MetavoxelInfo* nextInfo = &info; + do { + foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( + i - _spannerInputCount).getInlineValue()) { + Spanner* spanner = static_cast(object.data()); + if (spanner->isMasked() && !visit(spanner, info.minimum, info.size)) { + return SHORT_CIRCUIT; + } + } + } while ((nextInfo = nextInfo->parentInfo)); + } + } + return STOP_RECURSION; } RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, @@ -919,10 +998,11 @@ int RayIntersectionVisitor::visit(MetavoxelInfo& info) { } RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), - _spannerInputCount(spannerInputs.size()) { + const QVector& spannerInputs, const QVector& spannerMasks, + const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, inputs + spannerInputs + spannerMasks, outputs, lod), + _spannerInputCount(spannerInputs.size()), + _spannerMaskCount(spannerMasks.size()) { } void RaySpannerIntersectionVisitor::prepare() { @@ -941,12 +1021,12 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; - for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { + for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (spanner->testAndSetVisited()) { + if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited()) { SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) { + if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) { spannerDistances.append(spannerDistance); } } @@ -958,7 +1038,36 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { } } } - return info.isLeaf ? STOP_RECURSION : _order; + if (!info.isLeaf) { + return _order; + } + for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { + float maskValue = info.inputValues.at(i).getInlineValue(); + if (maskValue < 0.5f) { + const MetavoxelInfo* nextInfo = &info; + do { + foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( + i - _spannerInputCount).getInlineValue()) { + Spanner* spanner = static_cast(object.data()); + if (spanner->isMasked()) { + SpannerDistance spannerDistance = { spanner }; + if (spanner->findRayIntersection(_origin, _direction, + info.minimum, info.size, spannerDistance.distance)) { + spannerDistances.append(spannerDistance); + } + } + } + } while ((nextInfo = nextInfo->parentInfo)); + + qStableSort(spannerDistances); + foreach (const SpannerDistance& spannerDistance, spannerDistances) { + if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { + return SHORT_CIRCUIT; + } + } + } + } + return STOP_RECURSION; } DefaultMetavoxelGuide::DefaultMetavoxelGuide() { @@ -1224,6 +1333,7 @@ Spanner::Spanner() : _renderer(NULL), _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), + _masked(false), _lastVisit(0) { } @@ -1276,7 +1386,8 @@ SpannerRenderer* Spanner::getRenderer() { return _renderer; } -bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return _bounds.findRayIntersection(origin, direction, distance); } @@ -1297,11 +1408,12 @@ void SpannerRenderer::simulate(float deltaTime) { // nothing by default } -void SpannerRenderer::render(float alpha) { +void SpannerRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) { // nothing by default } -bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return false; } @@ -1424,7 +1536,8 @@ bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const { return true; } -bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); } @@ -1463,10 +1576,11 @@ void StaticModel::setURL(const QUrl& url) { } } -bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { // delegate to renderer, if we have one - return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : - Spanner::findRayIntersection(origin, direction, distance); + return _renderer ? _renderer->findRayIntersection(origin, direction, clipMinimum, clipSize, distance) : + Spanner::findRayIntersection(origin, direction, clipMinimum, clipSize, distance); } QByteArray StaticModel::getRendererClassName() const { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index fd7ce437cc..fa408aafb7 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -79,6 +79,11 @@ public: void toggle(const AttributePointer& attribute, const SharedObjectPointer& object); void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, + const SharedObjectPointer& newObject); + void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, + const SharedObjectPointer& newObject); + void clear(const AttributePointer& attribute); /// Convenience function that finds the first spanner intersecting the provided ray. @@ -255,13 +260,15 @@ class SpannerVisitor : public MetavoxelVisitor { public: SpannerVisitor(const QVector& spannerInputs, + const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD()); - /// Visits a spanner. + /// Visits a spanner (or part thereof). + /// \param clipSize the size of the clip volume, or zero if unclipped /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner) = 0; + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0; virtual void prepare(); virtual int visit(MetavoxelInfo& info); @@ -269,6 +276,7 @@ public: protected: int _spannerInputCount; + int _spannerMaskCount; }; /// Base class for ray intersection visitors. @@ -298,11 +306,12 @@ public: RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const QVector& spannerInputs, + const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD()); - /// Visits a spanner that the ray intersects. + /// Visits a spannerthat the ray intersects. /// \return true to continue, false to short-circuit the tour virtual bool visitSpanner(Spanner* spanner, float distance) = 0; @@ -312,6 +321,7 @@ public: protected: int _spannerInputCount; + int _spannerMaskCount; }; /// Interface for objects that guide metavoxel visitors. @@ -412,6 +422,7 @@ class Spanner : public SharedObject { Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) + Q_PROPERTY(float masked MEMBER _masked DESIGNABLE false) public: @@ -429,6 +440,9 @@ public: void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } float getVoxelizationGranularity() const { return _voxelizationGranularity; } + void setMasked(bool masked) { _masked = masked; } + bool isMasked() const { return _masked; } + /// Returns a reference to the list of attributes associated with this spanner. virtual const QVector& getAttributes() const; @@ -452,7 +466,9 @@ public: SpannerRenderer* getRenderer(); /// Finds the intersection between the described ray and this spanner. - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + /// \param clipSize the size of the clip region, or zero if unclipped + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; signals: @@ -471,6 +487,7 @@ private: Box _bounds; float _placementGranularity; float _voxelizationGranularity; + bool _masked; int _lastVisit; ///< the identifier of the last visit static int _visit; ///< the global visit counter @@ -486,8 +503,9 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(float alpha); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; }; /// An object with a 3D transform. @@ -539,7 +557,8 @@ public: virtual const QVector& getVoxelizedAttributes() const; virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; signals: @@ -572,7 +591,8 @@ public: void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize,float& distance) const; signals: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index ac4f8fc7f6..8ed97da54d 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -32,7 +32,8 @@ private: }; BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : - MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute() << + AttributeRegistry::getInstance()->getSpannerMaskAttribute()), _edit(edit) { } @@ -47,25 +48,68 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) { float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); if (volume >= 1.0f) { info.outputValues[0] = _edit.value; + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); return STOP_RECURSION; // entirely contained } if (info.size <= _edit.granularity) { if (volume >= 0.5f) { info.outputValues[0] = _edit.value; + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); } return STOP_RECURSION; // reached granularity limit; take best guess } return DEFAULT_ORDER; // subdivide } +class GatherUnmaskedSpannersVisitor : public SpannerVisitor { +public: + + GatherUnmaskedSpannersVisitor(const Box& bounds); + + const QList& getUnmaskedSpanners() const { return _unmaskedSpanners; } + + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + +private: + + Box _bounds; + QList _unmaskedSpanners; +}; + +GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), + _bounds(bounds) { +} + +bool GatherUnmaskedSpannersVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { + if (!spanner->isMasked() && spanner->getBounds().intersects(_bounds)) { + _unmaskedSpanners.append(spanner); + } + return true; +} + +static void setIntersectingMasked(const Box& bounds, MetavoxelData& data) { + GatherUnmaskedSpannersVisitor visitor(bounds); + data.guide(visitor); + + foreach (const SharedObjectPointer& object, visitor.getUnmaskedSpanners()) { + Spanner* newSpanner = static_cast(object->clone(true)); + newSpanner->setMasked(true); + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), object, newSpanner); + } +} + void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { data.expand(); } - BoxSetEditVisitor visitor(*this); - data.guide(visitor); + BoxSetEditVisitor setVisitor(*this); + data.guide(setVisitor); + + // flip the mask flag of all intersecting spanners + setIntersectingMasked(region, data); } GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) : @@ -180,25 +224,25 @@ ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : attribute(attribute) { } -class GetSpannerAttributesVisitor : public SpannerVisitor { +class GatherSpannerAttributesVisitor : public SpannerVisitor { public: - GetSpannerAttributesVisitor(const AttributePointer& attribute); + GatherSpannerAttributesVisitor(const AttributePointer& attribute); const QSet& getAttributes() const { return _attributes; } - virtual bool visit(Spanner* spanner); + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); protected: QSet _attributes; }; -GetSpannerAttributesVisitor::GetSpannerAttributesVisitor(const AttributePointer& attribute) : +GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) : SpannerVisitor(QVector() << attribute) { } -bool GetSpannerAttributesVisitor::visit(Spanner* spanner) { +bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) { _attributes.insert(attribute); } @@ -207,7 +251,7 @@ bool GetSpannerAttributesVisitor::visit(Spanner* spanner) { void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // find all the spanner attributes - GetSpannerAttributesVisitor visitor(attribute); + GatherSpannerAttributesVisitor visitor(attribute); data.guide(visitor); data.clear(attribute); @@ -233,12 +277,19 @@ private: }; SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(attributes, attributes), + MetavoxelVisitor(attributes, QVector() << attributes << + AttributeRegistry::getInstance()->getSpannerMaskAttribute()), _spanner(spanner) { } int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { - return _spanner->blendAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; + if (_spanner->blendAttributeValues(info)) { + return DEFAULT_ORDER; + } + if (info.outputValues.at(0).getAttribute()) { + info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline(1.0f)); + } + return STOP_RECURSION; } void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { @@ -251,4 +302,6 @@ void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& obje SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner); data.guide(visitor); + + setIntersectingMasked(spanner->getBounds(), data); } From 5aa9b981beab471004c737d0fa168a513cbcab73 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 31 Mar 2014 18:43:38 -0700 Subject: [PATCH 29/38] Hopefully, a fix for the Windows build. --- libraries/metavoxels/src/MetavoxelMessages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 8ed97da54d..ce0d01ccf2 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -168,7 +168,7 @@ UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector& attr _spanner(spanner), _voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f / AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()), - _steps(roundf(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) / + _steps(glm::round(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) / logf(2.0f) - 2.0f)) { } From fb37ef5bcaff1b91858b9e1b75bd92bb830a98b4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 1 Apr 2014 11:25:39 -0700 Subject: [PATCH 30/38] Fix for annoying intermittent seg fault on Linux exit. Closes #2542. --- interface/interface_en.ts | 16 ++++++++-------- interface/src/Application.cpp | 3 +++ interface/src/Audio.cpp | 8 +++++++- interface/src/Audio.h | 1 + 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index a1abb57b52..3f859c2cd1 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a237e92e2b..985e909f88 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -372,6 +372,9 @@ Application::~Application() { _nodeThread->quit(); _nodeThread->wait(); + // stop the audio process + QMetaObject::invokeMethod(&_audio, "stop"); + // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 9f993e653d..e4175cff48 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -308,6 +308,12 @@ void Audio::start() { } } +void Audio::stop() { + // "switch" to invalid devices in order to shut down the state + switchInputToAudioDevice(QAudioDeviceInfo()); + switchOutputToAudioDevice(QAudioDeviceInfo()); +} + QString Audio::getDefaultDeviceName(QAudio::Mode mode) { QAudioDeviceInfo deviceInfo = defaultAudioDeviceForMode(mode); return deviceInfo.deviceName(); @@ -846,7 +852,7 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) { // cleanup any previously initialized device if (_audioInput) { _audioInput->stop(); - disconnect(_inputDevice, 0, 0, 0); + disconnect(_inputDevice); _inputDevice = NULL; delete _audioInput; diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 170572a4d7..e728bc9645 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -76,6 +76,7 @@ public: public slots: void start(); + void stop(); void addReceivedAudioToBuffer(const QByteArray& audioByteArray); void handleAudioInput(); void reset(); From 36c41a1fd32c4c8eb7b4f70bfb03b11c186112f8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 1 Apr 2014 15:35:46 -0700 Subject: [PATCH 31/38] test other way of doing loudness --- interface/interface_en.ts | 12 ++++++------ interface/src/Application.cpp | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index a1abb57b52..709cea840d 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a237e92e2b..e0c26f3f14 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2540,6 +2540,13 @@ void Application::displayOverlay() { audioLevel = log2loudness / MAX_LOG2_SAMPLE * AUDIO_METER_SCALE_WIDTH; + + if (log2loudness <= 11.f) { + audioLevel = log2loudness / 11.f * AUDIO_METER_SCALE_WIDTH / 5.f; + } else { + audioLevel = (log2loudness - 10.f) * AUDIO_METER_SCALE_WIDTH / 5.f; + } + bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); glBegin(GL_QUADS); From c3934d9e8653f293ee96e0ccba3c371cfffccc2d Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 1 Apr 2014 15:49:08 -0700 Subject: [PATCH 32/38] Fix heap corruption. --- interface/src/Audio.cpp | 119 +++++++++++++++++++++++----------------- interface/src/Audio.h | 8 +++ 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 67f2e2caec..5e2524a54c 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -95,6 +95,7 @@ void Audio::reset() { QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) { + qDebug() << audioDevice.deviceName() << " " << deviceName; if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { result = audioDevice; } @@ -162,6 +163,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { qDebug() << "output device:" << woc.szPname; deviceName = woc.szPname; } + qDebug() << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; + return getNamedAudioDeviceForMode(mode, deviceName); #endif @@ -321,10 +324,12 @@ QVector Audio::getDeviceNames(QAudio::Mode mode) { } bool Audio::switchInputToAudioDevice(const QString& inputDeviceName) { + qDebug() << "DEBUG [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName)); } bool Audio::switchOutputToAudioDevice(const QString& outputDeviceName) { + qDebug() << "DEBUG [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName)); } @@ -343,7 +348,7 @@ void Audio::handleAudioInput() { QByteArray inputByteArray = _inputDevice->readAll(); - if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted) { + if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) { // if this person wants local loopback add that to the locally injected audio if (!_loopbackOutputDevice && _loopbackAudioOutput) { @@ -356,7 +361,7 @@ void Audio::handleAudioInput() { _loopbackOutputDevice->write(inputByteArray); } } else { - static float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate()) + float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate()) * (_outputFormat.channelCount() / _inputFormat.channelCount()); QByteArray loopBackByteArray(inputByteArray.size() * loopbackOutputToInputRatio, 0); @@ -381,9 +386,6 @@ void Audio::handleAudioInput() { // zero out the monoAudioSamples array and the locally injected audio memset(monoAudioSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); - // zero out the locally injected audio in preparation for audio procedural sounds - memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); - if (!_muted) { // we aren't muted, downsample the input audio linearResampling((int16_t*) inputAudioSamples, @@ -492,28 +494,8 @@ void Audio::handleAudioInput() { _lastInputLoudness = 0; } - // add procedural effects to the appropriate input samples - addProceduralSounds(monoAudioSamples, - NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - - if (!_proceduralOutputDevice && _proceduralAudioOutput) { - _proceduralOutputDevice = _proceduralAudioOutput->start(); - } - - // send whatever procedural sounds we want to locally loop back to the _proceduralOutputDevice - QByteArray proceduralOutput; - proceduralOutput.resize(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * _outputFormat.sampleRate() * - _outputFormat.channelCount() * sizeof(int16_t) / (_desiredInputFormat.sampleRate() * - _desiredInputFormat.channelCount())); - - linearResampling(_localProceduralSamples, - reinterpret_cast(proceduralOutput.data()), - NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL, - proceduralOutput.size() / sizeof(int16_t), - _desiredInputFormat, _outputFormat); - - if (_proceduralOutputDevice) { - _proceduralOutputDevice->write(proceduralOutput); + if (_proceduralAudioOutput) { + processProceduralAudio(monoAudioSamples, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); } NodeList* nodeList = NodeList::getInstance(); @@ -593,9 +575,43 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { } } + if (_audioOutput) { + // Audio output must exist and be correctly set up if we're going to process received audio + processReceivedAudio(audioByteArray); + } + + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size()); + + _lastReceiveTime = currentReceiveTime; +} + +bool Audio::mousePressEvent(int x, int y) { + if (_iconBounds.contains(x, y)) { + toggleMute(); + return true; + } + return false; +} + +void Audio::toggleMute() { + _muted = !_muted; + muteToggled(); +} + +void Audio::toggleAudioNoiseReduction() { + _noiseGateEnabled = !_noiseGateEnabled; +} + +void Audio::processReceivedAudio(const QByteArray& audioByteArray) +{ _ringBuffer.parseData(audioByteArray); - static float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate()) + float outSampleRate = _outputFormat.sampleRate(); + float desSampleRate = _desiredOutputFormat.sampleRate(); + int outChannelCount = _outputFormat.channelCount(); + int desChannelCount = _desiredOutputFormat.channelCount(); + float myOutputRatio = desSampleRate / outSampleRate * (desChannelCount / outChannelCount); + float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate()) * (_desiredOutputFormat.channelCount() / (float) _outputFormat.channelCount()); if (!_ringBuffer.isStarved() && _audioOutput && _audioOutput->bytesFree() == _audioOutput->bufferSize()) { @@ -650,33 +666,38 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { } delete[] ringBufferSamples; } - } - - Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size()); - - _lastReceiveTime = currentReceiveTime; } -bool Audio::mousePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - toggleMute(); - return true; +void Audio::processProceduralAudio(int16_t* monoInput, int numSamples) { + + // zero out the locally injected audio in preparation for audio procedural sounds + // This is correlated to numSamples, so it really needs to be numSamples * sizeof(sample) + memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); + // add procedural effects to the appropriate input samples + addProceduralSounds(monoInput, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + + if (!_proceduralOutputDevice) { + _proceduralOutputDevice = _proceduralAudioOutput->start(); + } + + // send whatever procedural sounds we want to locally loop back to the _proceduralOutputDevice + QByteArray proceduralOutput; + proceduralOutput.resize(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * _outputFormat.sampleRate() * + _outputFormat.channelCount() * sizeof(int16_t) / (_desiredInputFormat.sampleRate() * + _desiredInputFormat.channelCount())); + + linearResampling(_localProceduralSamples, + reinterpret_cast(proceduralOutput.data()), + NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL, + proceduralOutput.size() / sizeof(int16_t), + _desiredInputFormat, _outputFormat); + + if (_proceduralOutputDevice) { + _proceduralOutputDevice->write(proceduralOutput); } - return false; } -void Audio::toggleMute() { - _muted = !_muted; - muteToggled(); -} - -void Audio::toggleAudioNoiseReduction() { - _noiseGateEnabled = !_noiseGateEnabled; -} - - - // Take a pointer to the acquired microphone input samples and add procedural sounds void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) { float sample; diff --git a/interface/src/Audio.h b/interface/src/Audio.h index e1f2762ece..1e54fd22eb 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -165,9 +165,17 @@ private: // Audio callback in class context. inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight); + // Process procedural audio by + // 1. Echo to the local procedural output device + // 2. Mix with the audio input + void processProceduralAudio(int16_t* monoInput, int numSamples); + // Add sounds that we want the user to not hear themselves, by adding on top of mic input signal void addProceduralSounds(int16_t* monoInput, int numSamples); + // Process received audio + void processReceivedAudio(const QByteArray& audioByteArray); + bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo); bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo); }; From 865ca7de205d784fdd396c799952a23f5ca3167e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 1 Apr 2014 16:01:29 -0700 Subject: [PATCH 33/38] Align forearms with wrists. Closes #2577. --- interface/src/avatar/SkeletonModel.cpp | 43 +++++++++++--------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c44d2ebdea..ca18c62718 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -133,12 +133,17 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - setJointPosition(jointIndex, palm.getPosition()); float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; + int parentJointIndex = geometry.joints.at(jointIndex).parentIndex; + if (parentJointIndex == -1) { + return; + } + + // rotate forearm to align with palm direction glm::quat palmRotation; - getJointRotation(jointIndex, palmRotation, true); - applyRotationDelta(jointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false); - getJointRotation(jointIndex, palmRotation, true); + getJointRotation(parentJointIndex, palmRotation, true); + applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false); + getJointRotation(parentJointIndex, palmRotation, true); // sort the finger indices by raw x, get the average direction QVector fingerIndices; @@ -155,33 +160,21 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin } qSort(fingerIndices.begin(), fingerIndices.end()); - // rotate palm according to average finger direction + // rotate forearm according to average finger direction float directionLength = glm::length(direction); const unsigned int MIN_ROTATION_FINGERS = 3; if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) { - applyRotationDelta(jointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false); - getJointRotation(jointIndex, palmRotation, true); + applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false); + getJointRotation(parentJointIndex, palmRotation, true); } - // no point in continuing if there are no fingers - if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) { - return; - } + // let wrist inherit forearm rotation + _jointStates[jointIndex].rotation = glm::quat(); - // match them up as best we can - float proportion = fingerIndices.size() / (float)fingerJointIndices.size(); - for (int i = 0; i < fingerJointIndices.size(); i++) { - int fingerIndex = fingerIndices.at(roundf(i * proportion)).index; - glm::vec3 fingerVector = palm.getFingers()[fingerIndex].getTipPosition() - - palm.getFingers()[fingerIndex].getRootPosition(); - - int fingerJointIndex = fingerJointIndices.at(i); - int fingertipJointIndex = fingertipJointIndices.at(i); - glm::vec3 jointVector = extractTranslation(geometry.joints.at(fingertipJointIndex).bindTransform) - - extractTranslation(geometry.joints.at(fingerJointIndex).bindTransform); - - setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true); - } + // set elbow position from wrist position + glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f); + setJointPosition(parentJointIndex, palm.getPosition() + forearmVector * + geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale)); } void SkeletonModel::updateJointState(int index) { From 0b792985d3489dfb24e49c65cc0725f3c86a18e9 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 1 Apr 2014 16:43:18 -0700 Subject: [PATCH 34/38] Tweaks to VU meter, tone injector, and fix to noise gate --- interface/interface_en.ts | 4 +- interface/src/Application.cpp | 17 +++++---- interface/src/Audio.cpp | 69 +++++++++++++++++------------------ 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 709cea840d..f2efa8e9cb 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0c26f3f14..a685dfbb48 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2531,20 +2531,21 @@ void Application::displayOverlay() { const float CLIPPING_INDICATOR_TIME = 1.0f; const float AUDIO_METER_AVERAGING = 0.5; const float LOG2 = log(2.f); - const float MAX_LOG2_SAMPLE = 15.f; + const float METER_LOUDNESS_SCALE = 2.8f / 5.f; + const float LOG2_LOUDNESS_FLOOR = 11.f; float audioLevel = 0.f; float loudness = _audio.getLastInputLoudness() + 1.f; + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; - float log2loudness = log(_trailingAudioLoudness) / LOG2; - audioLevel = log2loudness / MAX_LOG2_SAMPLE * AUDIO_METER_SCALE_WIDTH; - - - if (log2loudness <= 11.f) { - audioLevel = log2loudness / 11.f * AUDIO_METER_SCALE_WIDTH / 5.f; + if (log2loudness <= LOG2_LOUDNESS_FLOOR) { + audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; } else { - audioLevel = (log2loudness - 10.f) * AUDIO_METER_SCALE_WIDTH / 5.f; + audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; + } + if (audioLevel > AUDIO_METER_SCALE_WIDTH) { + audioLevel = AUDIO_METER_SCALE_WIDTH; } bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 9f993e653d..5f6b320a8b 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -436,12 +436,12 @@ void Audio::handleAudioInput() { measuredDcOffset += monoAudioSamples[i]; monoAudioSamples[i] -= (int16_t) _dcOffset; thisSample = fabsf(monoAudioSamples[i]); - if (thisSample > (32767.f * CLIPPING_THRESHOLD)) { + if (thisSample >= (32767.f * CLIPPING_THRESHOLD)) { _timeSinceLastClip = 0.0f; } loudness += thisSample; // Noise Reduction: Count peaks above the average loudness - if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) { + if (_noiseGateEnabled && (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT))) { samplesOverNoiseGate++; } } @@ -454,32 +454,41 @@ void Audio::handleAudioInput() { _dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.f - DC_OFFSET_AVERAGING) * measuredDcOffset; } - // - _lastInputLoudness = fabs(loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - - float averageOfAllSampleFrames = 0.f; - _noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness; - if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) { - float smallestSample = FLT_MAX; - for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i+= NOISE_GATE_FRAMES_TO_AVERAGE) { - float thisAverage = 0.0f; - for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) { - thisAverage += _noiseSampleFrames[j]; - averageOfAllSampleFrames += _noiseSampleFrames[j]; - } - thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE; - - if (thisAverage < smallestSample) { - smallestSample = thisAverage; - } + // Add tone injection if enabled + const float TONE_FREQ = 220.f / SAMPLE_RATE * TWO_PI; + const float QUARTER_VOLUME = 8192.f; + if (_toneInjectionEnabled) { + loudness = 0.f; + for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { + monoAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample)); + loudness += fabsf(monoAudioSamples[i]); } - averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES; - _noiseGateMeasuredFloor = smallestSample; - _noiseGateSampleCounter = 0; - } + _lastInputLoudness = fabs(loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - if (_noiseGateEnabled) { + // If Noise Gate is enabled, check and turn the gate on and off + if (!_toneInjectionEnabled && _noiseGateEnabled) { + float averageOfAllSampleFrames = 0.f; + _noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness; + if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) { + float smallestSample = FLT_MAX; + for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i+= NOISE_GATE_FRAMES_TO_AVERAGE) { + float thisAverage = 0.0f; + for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) { + thisAverage += _noiseSampleFrames[j]; + averageOfAllSampleFrames += _noiseSampleFrames[j]; + } + thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE; + + if (thisAverage < smallestSample) { + smallestSample = thisAverage; + } + } + averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES; + _noiseGateMeasuredFloor = smallestSample; + _noiseGateSampleCounter = 0; + + } if (samplesOverNoiseGate > NOISE_GATE_WIDTH) { _noiseGateOpen = true; _noiseGateFramesToClose = NOISE_GATE_CLOSE_FRAME_DELAY; @@ -493,16 +502,6 @@ void Audio::handleAudioInput() { _lastInputLoudness = 0; } } - // - // Add tone injection if enabled - // - const float TONE_FREQ = 220.f / SAMPLE_RATE * TWO_PI; - const float QUARTER_VOLUME = 8192.f; - if (_toneInjectionEnabled) { - for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { - monoAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample)); - } - } // add input data just written to the scope QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection, From 952ca928f195d0535e602d8407f2174f1d1383ca Mon Sep 17 00:00:00 2001 From: matsukaze Date: Wed, 2 Apr 2014 12:28:22 -0700 Subject: [PATCH 35/38] More audio fixes. Fix audio buffer size on Win. --- interface/src/Audio.cpp | 67 +++++++++++++++++++++++++++++++---------- interface/src/Audio.h | 7 +++++ 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 5e2524a54c..8cb4ecfecd 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -282,8 +282,6 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples, } } -const int CALLBACK_ACCELERATOR_RATIO = 2; - void Audio::start() { // set up the desired audio format @@ -305,8 +303,11 @@ void Audio::start() { qDebug() << "The default audio output device is" << outputDeviceInfo.deviceName(); bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo); - if (!inputFormatSupported || !outputFormatSupported) { - qDebug() << "Unable to set up audio I/O because of a problem with input or output formats."; + if (!inputFormatSupported) { + qDebug() << "Unable to set up audio input because of a problem with input format."; + } + if (!outputFormatSupported) { + qDebug() << "Unable to set up audio output because of a problem with output format."; } } @@ -341,10 +342,9 @@ void Audio::handleAudioInput() { static int16_t* monoAudioSamples = (int16_t*) (monoAudioDataPacket + leadingBytes); - static float inputToNetworkInputRatio = _numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO - / NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; + float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes); - static unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio; + unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio; QByteArray inputByteArray = _inputDevice->readAll(); @@ -606,11 +606,6 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) { _ringBuffer.parseData(audioByteArray); - float outSampleRate = _outputFormat.sampleRate(); - float desSampleRate = _desiredOutputFormat.sampleRate(); - int outChannelCount = _outputFormat.channelCount(); - int desChannelCount = _desiredOutputFormat.channelCount(); - float myOutputRatio = desSampleRate / outSampleRate * (desChannelCount / outChannelCount); float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate()) * (_desiredOutputFormat.channelCount() / (float) _outputFormat.channelCount()); @@ -861,13 +856,12 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) { qDebug() << "The format to be used for audio input is" << _inputFormat; _audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); - _numInputCallbackBytes = NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL * _inputFormat.channelCount() - * (_inputFormat.sampleRate() / SAMPLE_RATE) - / CALLBACK_ACCELERATOR_RATIO; + _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); _audioInput->setBufferSize(_numInputCallbackBytes); // how do we want to handle input working, but output not working? - _inputRingBuffer.resizeForFrameSize(_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / sizeof(int16_t)); + int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); + _inputRingBuffer.resizeForFrameSize(numFrameSamples); _inputDevice = _audioInput->start(); connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput())); @@ -924,3 +918,44 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) } return supportedFormat; } + +// The following constant is operating system dependent due to differences in +// the way input audio is handled. The audio input buffer size is inversely +// proportional to the accelerator ratio. + +#ifdef Q_OS_WIN +const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.4f; +#endif + +#ifdef Q_OS_MAC +const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f; +#endif + +#ifdef Q_OS_LINUX +const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f; +#endif + +int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) +{ + int numInputCallbackBytes = (int)(((NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL + * format.channelCount() + * (format.sampleRate() / SAMPLE_RATE)) + / CALLBACK_ACCELERATOR_RATIO) + 0.5f); + + return numInputCallbackBytes; +} + +float Audio::calculateDeviceToNetworkInputRatio(int numBytes) +{ + float inputToNetworkInputRatio = (int)((_numInputCallbackBytes + * CALLBACK_ACCELERATOR_RATIO + / NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL) + 0.5f); + + return inputToNetworkInputRatio; +} + +int Audio::calculateNumberOfFrameSamples(int numBytes) +{ + int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / sizeof(int16_t); + return frameSamples; +} diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 1e54fd22eb..896ab3c3fb 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -178,6 +178,13 @@ private: bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo); bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo); + + // Callback acceleration dependent calculations + static const float CALLBACK_ACCELERATOR_RATIO; + int calculateNumberOfInputCallbackBytes(const QAudioFormat& format); + int calculateNumberOfFrameSamples(int numBytes); + float calculateDeviceToNetworkInputRatio(int numBytes); + }; From 94a23b7ffccd062ce9093ddd6c361f1517f54590 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Wed, 2 Apr 2014 14:15:20 -0700 Subject: [PATCH 36/38] Fix brace location and replace tab with space. --- interface/src/Audio.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 8cb4ecfecd..4b6cec2e50 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -95,7 +95,7 @@ void Audio::reset() { QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) { - qDebug() << audioDevice.deviceName() << " " << deviceName; + qDebug() << audioDevice.deviceName() << " " << deviceName; if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { result = audioDevice; } @@ -602,8 +602,7 @@ void Audio::toggleAudioNoiseReduction() { _noiseGateEnabled = !_noiseGateEnabled; } -void Audio::processReceivedAudio(const QByteArray& audioByteArray) -{ +void Audio::processReceivedAudio(const QByteArray& audioByteArray) { _ringBuffer.parseData(audioByteArray); float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate()) @@ -935,8 +934,7 @@ const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f; const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f; #endif -int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) -{ +int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) { int numInputCallbackBytes = (int)(((NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL * format.channelCount() * (format.sampleRate() / SAMPLE_RATE)) @@ -945,8 +943,7 @@ int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) return numInputCallbackBytes; } -float Audio::calculateDeviceToNetworkInputRatio(int numBytes) -{ +float Audio::calculateDeviceToNetworkInputRatio(int numBytes) { float inputToNetworkInputRatio = (int)((_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL) + 0.5f); @@ -954,8 +951,7 @@ float Audio::calculateDeviceToNetworkInputRatio(int numBytes) return inputToNetworkInputRatio; } -int Audio::calculateNumberOfFrameSamples(int numBytes) -{ +int Audio::calculateNumberOfFrameSamples(int numBytes) { int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / sizeof(int16_t); return frameSamples; } From 75429179223a579c2e8afb9f3a6b036b1361af4f Mon Sep 17 00:00:00 2001 From: matsukaze Date: Wed, 2 Apr 2014 14:22:17 -0700 Subject: [PATCH 37/38] Fix tab with space. --- interface/src/Audio.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 4b6cec2e50..c7e11b74d2 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -325,12 +325,12 @@ QVector Audio::getDeviceNames(QAudio::Mode mode) { } bool Audio::switchInputToAudioDevice(const QString& inputDeviceName) { - qDebug() << "DEBUG [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; + qDebug() << "DEBUG [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName)); } bool Audio::switchOutputToAudioDevice(const QString& outputDeviceName) { - qDebug() << "DEBUG [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; + qDebug() << "DEBUG [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName)); } @@ -682,10 +682,10 @@ void Audio::processProceduralAudio(int16_t* monoInput, int numSamples) { _desiredInputFormat.channelCount())); linearResampling(_localProceduralSamples, - reinterpret_cast(proceduralOutput.data()), - NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL, - proceduralOutput.size() / sizeof(int16_t), - _desiredInputFormat, _outputFormat); + reinterpret_cast(proceduralOutput.data()), + NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL, + proceduralOutput.size() / sizeof(int16_t), + _desiredInputFormat, _outputFormat); if (_proceduralOutputDevice) { _proceduralOutputDevice->write(proceduralOutput); From d28d686ceba67511f844eb4afb31d8c0f47e29e0 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Wed, 2 Apr 2014 17:45:22 -0700 Subject: [PATCH 38/38] Fix for missing voxel system on domain change. Fix for assertion failure thrown by Qt on exit. --- interface/src/ui/overlays/ImageOverlay.cpp | 9 +++++---- interface/src/ui/overlays/ImageOverlay.h | 2 ++ interface/src/voxels/VoxelSystem.cpp | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 178383749b..ac5c8ecefa 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -16,6 +16,7 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : + _manager(0), _textureID(0), _renderImage(false), _textureBound(false), @@ -33,9 +34,9 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? - QNetworkAccessManager* manager = new QNetworkAccessManager(this); - connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); - manager->get(QNetworkRequest(url)); + _manager = new QNetworkAccessManager(); + connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); + _manager->get(QNetworkRequest(url)); } void ImageOverlay::replyFinished(QNetworkReply* reply) { @@ -44,7 +45,7 @@ void ImageOverlay::replyFinished(QNetworkReply* reply) { QByteArray rawData = reply->readAll(); _textureImage.loadFromData(rawData); _renderImage = true; - + _manager->deleteLater(); } void ImageOverlay::render() { diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 77cac3b3c6..d6165e388d 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -49,6 +49,8 @@ private: QUrl _imageURL; QImage _textureImage; + QNetworkAccessManager* _manager; + GLuint _textureID; QRect _fromImage; // where from in the image to sample bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 5c68485436..bb907c8a9a 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -1508,7 +1508,9 @@ void VoxelSystem::killLocalVoxels() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelSystem::killLocalVoxels()"); _tree->lockForWrite(); + VoxelSystem* voxelSystem = _tree->getRoot()->getVoxelSystem(); _tree->eraseAllOctreeElements(); + _tree->getRoot()->setVoxelSystem(voxelSystem); _tree->unlock(); clearFreeBufferIndexes(); if (_usePrimitiveRenderer) {