From 64df9718f20a7371c0572ea024987eac5eb26b75 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 29 Jan 2014 16:36:44 -0800 Subject: [PATCH 01/24] Working on object attributes. --- interface/src/ui/MetavoxelEditor.cpp | 6 +- .../metavoxels/src/AttributeRegistry.cpp | 59 ++++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 54 +++++++++++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 7 +-- libraries/metavoxels/src/MetavoxelData.h | 14 +++-- 5 files changed, 127 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index d35b44ffef..0a168ff251 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -152,7 +153,10 @@ void MetavoxelEditor::updateValueEditor() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); QWidget* editor = attribute->createEditor(); if (editor) { - _value->layout()->addWidget(editor); + QScrollArea* area = new QScrollArea(); + area->setWidgetResizable(true); + area->setWidget(editor); + _value->layout()->addWidget(area); } } diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 5b7a8859ca..f57e3ca925 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -7,6 +7,9 @@ // #include +#include +#include +#include #include #include #include @@ -22,7 +25,8 @@ AttributeRegistry* AttributeRegistry::getInstance() { } AttributeRegistry::AttributeRegistry() : - _guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))), + _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, + PolymorphicDataPointer(new DefaultMetavoxelGuide())))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { } @@ -181,6 +185,59 @@ PolymorphicAttribute::PolymorphicAttribute(const QString& name, const Polymorphi InlineAttribute(name, defaultValue) { } +void* PolymorphicAttribute::createFromVariant(const QVariant& value) const { + return create(encodeInline(value.value())); +} + bool PolymorphicAttribute::merge(void*& parent, void* children[]) const { return false; } + +PolymorphicData* SharedObject::clone() const { + // default behavior is to make a copy using the no-arg constructor and copy the stored properties + const QMetaObject* metaObject = this->metaObject(); + QObject* newObject = metaObject->newInstance(); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored()) { + property.write(newObject, property.read(this)); + } + } + foreach (const QByteArray& propertyName, dynamicPropertyNames()) { + newObject->setProperty(propertyName, property(propertyName)); + } + return static_cast(newObject); +} + +SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, + const PolymorphicDataPointer& defaultValue) : + PolymorphicAttribute(name, defaultValue), + _metaObject(metaObject) { + +} + +QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { + SharedObjectEditor* editor = new SharedObjectEditor(parent); + editor->setObject(_defaultValue); + return editor; +} + +SharedObjectEditor::SharedObjectEditor(QWidget* parent) : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(); + setLayout(layout); + + QFormLayout* form = new QFormLayout(); + layout->addLayout(form); + + form->addRow("Type:", _type = new QComboBox()); + _type->addItem("(none)"); + connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); +} + +void SharedObjectEditor::setObject(const PolymorphicDataPointer& object) { + _object = object; +} + +void SharedObjectEditor::updateType() { + +} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 058b02d78f..c27e24395c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -20,6 +20,7 @@ #include "Bitstream.h" +class QComboBox; class QPushButton; class QScriptContext; class QScriptEngine; @@ -338,13 +339,66 @@ template<> PolymorphicData* QExplicitlySharedDataPointer::clone typedef QExplicitlySharedDataPointer PolymorphicDataPointer; +Q_DECLARE_METATYPE(PolymorphicDataPointer) + /// Provides polymorphic streaming and averaging. class PolymorphicAttribute : public InlineAttribute { public: PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer()); + virtual void* createFromVariant(const QVariant& value) const; + virtual bool merge(void*& parent, void* children[]) const; }; +class SharedObject : public QObject, public PolymorphicData { + Q_OBJECT + +public: + + /// Creates a new clone of this object. + virtual PolymorphicData* clone() const; +}; + +/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). +class SharedObjectAttribute : public PolymorphicAttribute { + Q_OBJECT + Q_PROPERTY(const QMetaObject* metaObject MEMBER _metaObject) + +public: + + Q_INVOKABLE SharedObjectAttribute(const QString& name = QString(), const QMetaObject* metaObject = NULL, + const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer()); + + virtual QWidget* createEditor(QWidget* parent = NULL) const; + +private: + + const QMetaObject* _metaObject; +}; + +/// Allows editing shared object instances. +class SharedObjectEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(PolymorphicDataPointer object MEMBER _object WRITE setObject USER true) + +public: + + SharedObjectEditor(QWidget* parent); + +public slots: + + void setObject(const PolymorphicDataPointer& object); + +private slots: + + void updateType(); + +private: + + QComboBox* _type; + PolymorphicDataPointer _object; +}; + #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2f99684a42..8f3215bc84 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -425,8 +425,7 @@ MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, cons MetavoxelVisitor::~MetavoxelVisitor() { } -PolymorphicData* DefaultMetavoxelGuide::clone() const { - return new DefaultMetavoxelGuide(); +DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { @@ -592,10 +591,6 @@ ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction _info.setProperty(_minimumHandle, _minimum); } -PolymorphicData* ScriptedMetavoxelGuide::clone() const { - return new ScriptedMetavoxelGuide(_guideFunction); -} - void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue(this)); _getInputsFunction.setData(data); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 0114f1b4d6..06ecec0001 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -150,7 +150,9 @@ protected: typedef QSharedPointer MetavoxelVisitorPointer; /// Interface for objects that guide metavoxel visitors. -class MetavoxelGuide : public PolymorphicData { +class MetavoxelGuide : public SharedObject { + Q_OBJECT + public: /// Guides the specified visitor to the contained voxels. @@ -159,21 +161,23 @@ public: /// Guides visitors through the explicit content of the system. class DefaultMetavoxelGuide : public MetavoxelGuide { + Q_OBJECT + public: - virtual PolymorphicData* clone() const; + Q_INVOKABLE DefaultMetavoxelGuide(); virtual void guide(MetavoxelVisitation& visitation); }; /// Represents a guide implemented in Javascript. class ScriptedMetavoxelGuide : public MetavoxelGuide { + Q_OBJECT + public: - ScriptedMetavoxelGuide(const QScriptValue& guideFunction); + Q_INVOKABLE ScriptedMetavoxelGuide(const QScriptValue& guideFunction = QScriptValue()); - virtual PolymorphicData* clone() const; - virtual void guide(MetavoxelVisitation& visitation); private: From a9905cf87054186ac16794399cd9073ff7a46979 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 29 Jan 2014 19:35:43 -0800 Subject: [PATCH 02/24] Working on editing QObjects. --- .../metavoxels/src/AttributeRegistry.cpp | 36 ++++++++++++++++--- libraries/metavoxels/src/AttributeRegistry.h | 5 ++- libraries/metavoxels/src/Bitstream.cpp | 10 ++++++ libraries/metavoxels/src/Bitstream.h | 4 +++ libraries/metavoxels/src/MetavoxelData.cpp | 3 ++ 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index f57e3ca925..8bd06a634b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -217,20 +218,23 @@ SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObj } QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { - SharedObjectEditor* editor = new SharedObjectEditor(parent); + SharedObjectEditor* editor = new SharedObjectEditor(_metaObject, parent); editor->setObject(_defaultValue); return editor; } -SharedObjectEditor::SharedObjectEditor(QWidget* parent) : QWidget(parent) { +SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); - QFormLayout* form = new QFormLayout(); + QFormLayout* form = new QFormLayout(); layout->addLayout(form); form->addRow("Type:", _type = new QComboBox()); _type->addItem("(none)"); + foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { + _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); + } connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); } @@ -239,5 +243,29 @@ void SharedObjectEditor::setObject(const PolymorphicDataPointer& object) { } void SharedObjectEditor::updateType() { - + // delete the existing rows + if (layout()->count() > 1) { + QFormLayout* form = static_cast(layout()->takeAt(1)); + while (!form->isEmpty()) { + QLayoutItem* item = form->takeAt(0); + if (item->widget()) { + delete item->widget(); + } + delete item; + } + delete form; + } + const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); + if (metaObject == NULL) { + return; + } + QFormLayout* form = new QFormLayout(); + static_cast(layout())->addLayout(form); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL); + if (widget) { + form->addRow(property.name(), widget); + } + } } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index c27e24395c..01ea7d03dd 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -21,6 +21,7 @@ #include "Bitstream.h" class QComboBox; +class QFormLayout; class QPushButton; class QScriptContext; class QScriptEngine; @@ -378,6 +379,8 @@ private: const QMetaObject* _metaObject; }; +Q_DECLARE_METATYPE(const QMetaObject*) + /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT @@ -385,7 +388,7 @@ class SharedObjectEditor : public QWidget { public: - SharedObjectEditor(QWidget* parent); + SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent); public slots: diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 4f23c79d78..afa1757145 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -52,6 +52,7 @@ IDStreamer& IDStreamer::operator>>(int& value) { int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); + getMetaObjectSubClasses().insert(metaObject->superClass(), metaObject); return 0; } @@ -61,6 +62,10 @@ int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { return 0; } +QList Bitstream::getMetaObjectSubClasses(const QMetaObject* metaObject) { + return getMetaObjectSubClasses().values(metaObject); +} + Bitstream::Bitstream(QDataStream& underlying) : _underlying(underlying), _byte(0), @@ -351,6 +356,11 @@ QHash& Bitstream::getMetaObjects() { return metaObjects; } +QMultiHash& Bitstream::getMetaObjectSubClasses() { + static QMultiHash metaObjectSubClasses; + return metaObjectSubClasses; +} + QHash& Bitstream::getTypeStreamers() { static QHash typeStreamers; return typeStreamers; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 5764cf2cdf..a564c7a6f1 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -183,6 +183,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 list of registered subclasses for the supplied meta-object. + static QList getMetaObjectSubClasses(const QMetaObject* metaObject); + /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. Bitstream(QDataStream& underlying); @@ -267,6 +270,7 @@ private: RepeatedValueStreamer _attributeStreamer; static QHash& getMetaObjects(); + static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); }; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 8f3215bc84..a29341622b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -12,6 +12,9 @@ #include "MetavoxelData.h" #include "MetavoxelUtil.h" +REGISTER_META_OBJECT(DefaultMetavoxelGuide) +REGISTER_META_OBJECT(ScriptedMetavoxelGuide) + MetavoxelData::MetavoxelData() : _size(1.0f) { } From 8df4e884e43de2543a4ab60b3db9b52b63abe248 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Jan 2014 11:15:03 -0800 Subject: [PATCH 03/24] Fix box color on hovering. --- interface/src/ui/MetavoxelEditor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 0a168ff251..2c9002c4d5 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -278,9 +278,13 @@ void MetavoxelEditor::render() { glutSolidCube(1.0); glDisable(GL_CULL_FACE); } + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); glutWireCube(1.0); glPopMatrix(); + + } else { + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); } glLineWidth(1.0f); @@ -296,7 +300,6 @@ void MetavoxelEditor::render() { _gridProgram.bind(); - glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); _gridProgram.release(); From a7ebcf5a964596a1ea6d1fa00cb968d58eece337 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Jan 2014 11:24:24 -0800 Subject: [PATCH 04/24] Layout fix. --- interface/src/ui/MetavoxelEditor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 2c9002c4d5..1df6d72c4e 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -147,7 +147,9 @@ void MetavoxelEditor::updateValueEditor() { _value->setVisible(true); if (!_value->layout()->isEmpty()) { - delete _value->layout()->takeAt(0); + QLayoutItem* item = _value->layout()->takeAt(0); + delete item->widget(); + delete item; } AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); From 5a194c4c015e4ada20d40e31be727c35c64b9656 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Jan 2014 18:53:51 -0800 Subject: [PATCH 05/24] Working on parameterizable URLs, etc. --- .../metavoxels/src/AttributeRegistry.cpp | 112 +++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 9 +- libraries/metavoxels/src/MetavoxelData.cpp | 31 +++-- libraries/metavoxels/src/MetavoxelData.h | 5 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 71 +++++++++++ libraries/metavoxels/src/MetavoxelUtil.h | 45 +++++++ 6 files changed, 256 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 8bd06a634b..bc52c09444 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -190,8 +190,39 @@ void* PolymorphicAttribute::createFromVariant(const QVariant& value) const { return create(encodeInline(value.value())); } +bool PolymorphicAttribute::equal(void* first, void* second) const { + PolymorphicDataPointer firstPointer = decodeInline(first); + PolymorphicDataPointer secondPointer = decodeInline(second); + return (firstPointer == secondPointer) || (firstPointer && secondPointer && + firstPointer->equals(secondPointer.constData())); +} + bool PolymorphicAttribute::merge(void*& parent, void* children[]) const { - return false; + PolymorphicDataPointer& parentPointer = *(PolymorphicDataPointer*)&parent; + PolymorphicDataPointer childPointers[MERGE_COUNT]; + for (int i = 0; i < MERGE_COUNT; i++) { + childPointers[i] = decodeInline(children[i]); + } + if (childPointers[0]) { + for (int i = 1; i < MERGE_COUNT; i++) { + if (childPointers[0] == childPointers[i]) { + continue; + } + if (!(childPointers[i] && childPointers[0]->equals(childPointers[i].constData()))) { + parentPointer = _defaultValue; + return false; + } + } + } else { + for (int i = 1; i < MERGE_COUNT; i++) { + if (childPointers[i]) { + parentPointer = _defaultValue; + return false; + } + } + } + parentPointer = childPointers[0]; + return true; } PolymorphicData* SharedObject::clone() const { @@ -210,6 +241,31 @@ PolymorphicData* SharedObject::clone() const { return static_cast(newObject); } +bool SharedObject::equals(const PolymorphicData* other) const { + // default behavior is to compare the properties + const QMetaObject* metaObject = this->metaObject(); + const SharedObject* sharedOther = static_cast(other); + if (metaObject != sharedOther->metaObject()) { + return false; + } + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored() && property.read(this) != property.read(sharedOther)) { + return false; + } + } + QList dynamicPropertyNames = this->dynamicPropertyNames(); + if (dynamicPropertyNames.size() != sharedOther->dynamicPropertyNames().size()) { + return false; + } + foreach (const QByteArray& propertyName, dynamicPropertyNames) { + if (property(propertyName) != sharedOther->property(propertyName)) { + return false; + } + } + return true; +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const PolymorphicDataPointer& defaultValue) : PolymorphicAttribute(name, defaultValue), @@ -240,6 +296,23 @@ SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* p void SharedObjectEditor::setObject(const PolymorphicDataPointer& object) { _object = object; + const QMetaObject* metaObject = object ? static_cast(object.data())->metaObject() : NULL; + int index = _type->findData(QVariant::fromValue(metaObject)); + if (index != -1) { + // ensure that we call updateType to obtain the values + if (_type->currentIndex() == index) { + updateType(); + } else { + _type->setCurrentIndex(index); + } + } +} + +const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) { + while (propertyIndex < metaObject->propertyOffset()) { + metaObject = metaObject->superClass(); + } + return metaObject; } void SharedObjectEditor::updateType() { @@ -257,15 +330,50 @@ void SharedObjectEditor::updateType() { } const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); if (metaObject == NULL) { + _object.reset(); return; } + QObject* oldObject = static_cast(_object.data()); + const QMetaObject* oldMetaObject = oldObject ? oldObject->metaObject() : NULL; + QObject* newObject = metaObject->newInstance(); + QFormLayout* form = new QFormLayout(); static_cast(layout())->addLayout(form); for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); + if (oldMetaObject && i < oldMetaObject->propertyCount() && + getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) { + // copy the state of the shared ancestry + property.write(newObject, property.read(oldObject)); + } QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL); if (widget) { - form->addRow(property.name(), widget); + widget->setProperty("propertyIndex", i); + form->addRow(QByteArray(property.name()) + ':', widget); + QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); + const QMetaObject* widgetMetaObject = widget->metaObject(); + QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); + widgetProperty.write(widget, property.read(newObject)); + if (widgetProperty.hasNotifySignal()) { + connect(widget, QByteArray(SIGNAL()).append(widgetProperty.notifySignal().methodSignature()), + SLOT(propertyChanged())); + } } } + _object = static_cast(newObject); +} + +void SharedObjectEditor::propertyChanged() { + QFormLayout* form = static_cast(layout()->itemAt(1)); + for (int i = 0; i < form->rowCount(); i++) { + QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); + if (widget != sender()) { + continue; + } + _object.detach(); + QObject* object = static_cast(_object.data()); + QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); + QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); + property.write(object, widget->property(valuePropertyName)); + } } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 01ea7d03dd..b1635e2f92 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -334,6 +334,9 @@ public: /// Creates a new clone of this object. virtual PolymorphicData* clone() const = 0; + + /// Tests this object for equality with another. + virtual bool equals(const PolymorphicData* other) const = 0; }; template<> PolymorphicData* QExplicitlySharedDataPointer::clone(); @@ -350,6 +353,8 @@ public: virtual void* createFromVariant(const QVariant& value) const; + virtual bool equal(void* first, void* second) const; + virtual bool merge(void*& parent, void* children[]) const; }; @@ -358,8 +363,9 @@ class SharedObject : public QObject, public PolymorphicData { public: - /// Creates a new clone of this object. virtual PolymorphicData* clone() const; + + virtual bool equals(const PolymorphicData* other) const; }; /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). @@ -397,6 +403,7 @@ public slots: private slots: void updateType(); + void propertyChanged(); private: diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index a29341622b..b8509233c6 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -572,20 +572,25 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin } ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction) : - _guideFunction(guideFunction), - _minimumHandle(guideFunction.engine()->toStringHandle("minimum")), - _sizeHandle(guideFunction.engine()->toStringHandle("size")), - _inputValuesHandle(guideFunction.engine()->toStringHandle("inputValues")), - _outputValuesHandle(guideFunction.engine()->toStringHandle("outputValues")), - _isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")), - _getInputsFunction(guideFunction.engine()->newFunction(getInputs, 0)), - _getOutputsFunction(guideFunction.engine()->newFunction(getOutputs, 0)), - _visitFunction(guideFunction.engine()->newFunction(visit, 1)), - _info(guideFunction.engine()->newObject()), - _minimum(guideFunction.engine()->newArray(3)) { + _guideFunction(guideFunction) { - _arguments.append(guideFunction.engine()->newObject()); - QScriptValue visitor = guideFunction.engine()->newObject(); + QScriptEngine* engine = guideFunction.engine(); + if (!engine) { + return; + } + _minimumHandle = engine->toStringHandle("minimum"); + _sizeHandle = engine->toStringHandle("size"); + _inputValuesHandle = engine->toStringHandle("inputValues"); + _outputValuesHandle = engine->toStringHandle("outputValues"); + _isLeafHandle = engine->toStringHandle("isLeaf"); + _getInputsFunction = engine->newFunction(getInputs, 0); + _getOutputsFunction = engine->newFunction(getOutputs, 0); + _visitFunction = engine->newFunction(visit, 1); + _info = engine->newObject(); + _minimum = engine->newArray(3); + + _arguments.append(engine->newObject()); + QScriptValue visitor = engine->newObject(); visitor.setProperty("getInputs", _getInputsFunction); visitor.setProperty("getOutputs", _getOutputsFunction); visitor.setProperty("visit", _visitFunction); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 06ecec0001..0d6fe3be1e 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -19,10 +19,10 @@ #include #include "AttributeRegistry.h" +#include "MetavoxelUtil.h" class QScriptContext; -class Box; class MetavoxelNode; class MetavoxelVisitation; class MetavoxelVisitor; @@ -173,6 +173,7 @@ public: /// Represents a guide implemented in Javascript. class ScriptedMetavoxelGuide : public MetavoxelGuide { Q_OBJECT + Q_PROPERTY(ParameterizedURL url MEMBER _url) public: @@ -186,6 +187,8 @@ private: static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine); static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); + ParameterizedURL _url; + QScriptValue _guideFunction; QScriptString _minimumHandle; QScriptString _sizeHandle; diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index d337b0cc3f..ba6cbfa0c5 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -7,6 +7,10 @@ // #include +#include +#include +#include +#include #include #include @@ -14,6 +18,47 @@ #include "MetavoxelUtil.h" +class DelegatingItemEditorFactory : public QItemEditorFactory { +public: + + DelegatingItemEditorFactory(); + + virtual QWidget* createEditor(int userType, QWidget* parent) const; + virtual QByteArray valuePropertyName(int userType) const; + +private: + + const QItemEditorFactory* _parentFactory; +}; + +DelegatingItemEditorFactory::DelegatingItemEditorFactory() : + _parentFactory(QItemEditorFactory::defaultFactory()) { +} + +QWidget* DelegatingItemEditorFactory::createEditor(int userType, QWidget* parent) const { + QWidget* editor = QItemEditorFactory::createEditor(userType, parent); + return (editor == NULL) ? _parentFactory->createEditor(userType, parent) : editor; +} + +QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const { + QByteArray propertyName = QItemEditorFactory::valuePropertyName(userType); + return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName; +} + +static QItemEditorFactory* getItemEditorFactory() { + static QItemEditorFactory* factory = new DelegatingItemEditorFactory(); + QItemEditorFactory::setDefaultFactory(factory); + return factory; +} + +static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { + QItemEditorCreatorBase* creator = new QStandardItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + +static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); + QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) { // get the header size int headerSize = numBytesForPacketHeader(data); @@ -33,3 +78,29 @@ bool Box::contains(const Box& other) const { other.minimum.y >= minimum.y && other.maximum.y <= maximum.y && other.minimum.z >= minimum.z && other.maximum.z <= maximum.z; } + +ParameterizedURL::ParameterizedURL(const QUrl& url, const QVariantHash& parameters) : + _url(url), + _parameters(parameters) { +} + +bool ParameterizedURL::operator==(const ParameterizedURL& other) const { + return _url == other._url && _parameters == other._parameters; +} + +bool ParameterizedURL::operator!=(const ParameterizedURL& other) const { + return _url != other._url || _parameters != other._parameters; +} + +ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) : + QWidget(parent) { + + QVBoxLayout* layout = new QVBoxLayout(); + setLayout(layout); + + layout->addWidget(_line = new QLineEdit()); +} + +void ParameterizedURLEditor::setURL(const ParameterizedURL& url) { + _url = url; +} diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index d97826fb93..27cd1b5deb 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -9,11 +9,14 @@ #ifndef __interface__MetavoxelUtil__ #define __interface__MetavoxelUtil__ +#include #include +#include #include "Bitstream.h" class QByteArray; +class QLineEdit; class HifiSockAddr; @@ -36,4 +39,46 @@ public: DECLARE_STREAMABLE_METATYPE(Box) +/// Combines a URL with a set of typed parameters. +class ParameterizedURL { +public: + + ParameterizedURL(const QUrl& url = QUrl(), const QVariantHash& parameters = QVariantHash()); + + void setURL(const QUrl& url) { _url = url; } + const QUrl& getURL() const { return _url; } + + void setParameters(const QVariantHash& parameters) { _parameters = parameters; } + const QVariantHash& getParameters() const { return _parameters; } + + bool operator==(const ParameterizedURL& other) const; + bool operator!=(const ParameterizedURL& other) const; + +private: + + QUrl _url; + QVariantHash _parameters; +}; + +Q_DECLARE_METATYPE(ParameterizedURL) + +/// Allows editing parameterized URLs. +class ParameterizedURLEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL USER true) + +public: + + ParameterizedURLEditor(QWidget* parent = NULL); + +public slots: + + void setURL(const ParameterizedURL& url); + +private: + + ParameterizedURL _url; + QLineEdit* _line; +}; + #endif /* defined(__interface__MetavoxelUtil__) */ From f85542e0e749774d3eb320517d05d92d9787a03d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 31 Jan 2014 11:39:16 -0800 Subject: [PATCH 06/24] Working on a test guide. --- libraries/metavoxels/src/AttributeRegistry.cpp | 2 +- libraries/metavoxels/src/Bitstream.cpp | 6 +++++- libraries/metavoxels/src/MetavoxelData.cpp | 10 +++++++++- libraries/metavoxels/src/MetavoxelData.h | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index bc52c09444..550984b3c9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -339,7 +339,7 @@ void SharedObjectEditor::updateType() { QFormLayout* form = new QFormLayout(); static_cast(layout())->addLayout(form); - for (int i = 0; i < metaObject->propertyCount(); i++) { + for (int i = QObject::staticMetaObject.propertyCount(); i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (oldMetaObject && i < oldMetaObject->propertyCount() && getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) { diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index afa1757145..9fed9871f6 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -52,7 +52,11 @@ IDStreamer& IDStreamer::operator>>(int& value) { int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); - getMetaObjectSubClasses().insert(metaObject->superClass(), metaObject); + + // register it as a subclass of all of its superclasses + for (const QMetaObject* superClass = metaObject->superClass(); superClass != NULL; superClass = superClass->superClass()) { + getMetaObjectSubClasses().insert(superClass, metaObject); + } return 0; } diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index b8509233c6..dba34465aa 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -13,7 +13,7 @@ #include "MetavoxelUtil.h" REGISTER_META_OBJECT(DefaultMetavoxelGuide) -REGISTER_META_OBJECT(ScriptedMetavoxelGuide) +REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) MetavoxelData::MetavoxelData() : _size(1.0f) { } @@ -516,6 +516,14 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } +ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(1.0) { +} + +void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { + DefaultMetavoxelGuide::guide(visitation); + +} + static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, const QVector& attributes) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 0d6fe3be1e..658f83f38f 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -170,6 +170,22 @@ public: virtual void guide(MetavoxelVisitation& visitation); }; +/// A temporary test guide that just makes the existing voxels throb with delight. +class ThrobbingMetavoxelGuide : public DefaultMetavoxelGuide { + Q_OBJECT + Q_PROPERTY(double rate MEMBER _rate) + +public: + + Q_INVOKABLE ThrobbingMetavoxelGuide(); + + virtual void guide(MetavoxelVisitation& visitation); + +private: + + double _rate; +}; + /// Represents a guide implemented in Javascript. class ScriptedMetavoxelGuide : public MetavoxelGuide { Q_OBJECT From 449cf1fbd3c440456d7a37615acae17abbfd4f2a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 3 Feb 2014 12:45:45 -0800 Subject: [PATCH 07/24] Layout fix. --- .../metavoxels/src/AttributeRegistry.cpp | 1 + libraries/metavoxels/src/MetavoxelUtil.cpp | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 550984b3c9..9a18ecc8b9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -281,6 +281,7 @@ QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); setLayout(layout); QFormLayout* form = new QFormLayout(); diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index ba6cbfa0c5..2c20d6fda0 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -7,6 +7,7 @@ // #include +#include #include #include #include @@ -31,8 +32,20 @@ private: const QItemEditorFactory* _parentFactory; }; +class DoubleEditor : public QDoubleSpinBox { +public: + + DoubleEditor(QWidget* parent = NULL); +}; + +DoubleEditor::DoubleEditor(QWidget* parent) : QDoubleSpinBox(parent) { + setMinimum(-FLT_MAX); +} + DelegatingItemEditorFactory::DelegatingItemEditorFactory() : _parentFactory(QItemEditorFactory::defaultFactory()) { + + QItemEditorFactory::setDefaultFactory(this); } QWidget* DelegatingItemEditorFactory::createEditor(int userType, QWidget* parent) const { @@ -46,9 +59,15 @@ QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const { } static QItemEditorFactory* getItemEditorFactory() { - static QItemEditorFactory* factory = new DelegatingItemEditorFactory(); - QItemEditorFactory::setDefaultFactory(factory); - return factory; + static DelegatingItemEditorFactory factory; + return &factory; +} + +static QItemEditorCreatorBase* createDoubleEditorCreator() { + QItemEditorCreatorBase* creator = new QStandardItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; } static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { @@ -57,6 +76,7 @@ static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { return creator; } +static QItemEditorCreatorBase* doubleEditorCreator = createDoubleEditorCreator(); static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) { From 7dfb83b6cccc51e51c94c116856e4c3871da046e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 3 Feb 2014 14:35:29 -0800 Subject: [PATCH 08/24] Fix for obtaining values from editor. --- interface/src/ui/MetavoxelEditor.cpp | 21 +++++++------------ interface/src/ui/MetavoxelEditor.h | 2 ++ .../metavoxels/src/AttributeRegistry.cpp | 6 ++++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 1df6d72c4e..c97832d791 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -81,6 +81,9 @@ MetavoxelEditor::MetavoxelEditor() : QVBoxLayout* valueLayout = new QVBoxLayout(); _value->setLayout(valueLayout); + valueLayout->addWidget(_valueArea = new QScrollArea()); + _valueArea->setWidgetResizable(true); + updateAttributes(); connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); @@ -146,19 +149,14 @@ void MetavoxelEditor::updateValueEditor() { } _value->setVisible(true); - if (!_value->layout()->isEmpty()) { - QLayoutItem* item = _value->layout()->takeAt(0); - delete item->widget(); - delete item; + if (_valueArea->widget()) { + delete _valueArea->widget(); } AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); QWidget* editor = attribute->createEditor(); if (editor) { - QScrollArea* area = new QScrollArea(); - area->setWidgetResizable(true); - area->setWidget(editor); - _value->layout()->addWidget(area); + _valueArea->setWidget(editor); } } @@ -372,11 +370,8 @@ void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maxi } QVariant MetavoxelEditor::getValue() const { - if (_value->layout()->isEmpty()) { - return QVariant(); - } - QWidget* editor = _value->layout()->itemAt(0)->widget(); - return editor->metaObject()->userProperty().read(editor); + QWidget* editor = _valueArea->widget(); + return editor ? editor->metaObject()->userProperty().read(editor) : QVariant(); } ProgramObject MetavoxelEditor::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 6fe5b26398..9024837757 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -17,6 +17,7 @@ class QComboBox; class QDoubleSpinBox; class QGroupBox; class QListWidget; +class QScrollArea; /// Allows editing metavoxels. class MetavoxelEditor : public QDialog { @@ -52,6 +53,7 @@ private: QDoubleSpinBox* _gridSpacing; QDoubleSpinBox* _gridPosition; QGroupBox* _value; + QScrollArea* _valueArea; enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE }; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9a18ecc8b9..c6cad2bbaf 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -157,8 +157,10 @@ QWidget* QRgbAttribute::createEditor(QWidget* parent) const { } QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { - setLayout(new QVBoxLayout()); - layout()->addWidget(_button = new QPushButton()); + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + layout->addWidget(_button = new QPushButton()); connect(_button, SIGNAL(clicked()), SLOT(selectColor())); } From 3f529e37b55ebaf65703f3f684d2784882a07ad6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 3 Feb 2014 16:34:04 -0800 Subject: [PATCH 09/24] Working on streaming objects. --- libraries/metavoxels/src/AttributeRegistry.cpp | 14 ++++++++++++++ libraries/metavoxels/src/AttributeRegistry.h | 5 +++-- libraries/metavoxels/src/Bitstream.cpp | 5 +++++ libraries/metavoxels/src/Bitstream.h | 2 ++ libraries/metavoxels/src/MetavoxelData.cpp | 12 ++++++++++++ libraries/metavoxels/src/MetavoxelUtil.cpp | 4 ++-- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c6cad2bbaf..d4995109fb 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -275,6 +275,20 @@ SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObj } +void SharedObjectAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + QObject* object; + in >> object; + *((PolymorphicDataPointer*)&value) = static_cast(object); + } +} + +void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + out << static_cast(decodeInline(value).data()); + } +} + QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { SharedObjectEditor* editor = new SharedObjectEditor(_metaObject, parent); editor->setObject(_defaultValue); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index b1635e2f92..37c17934b3 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -378,6 +378,9 @@ public: Q_INVOKABLE SharedObjectAttribute(const QString& name = QString(), const QMetaObject* metaObject = NULL, const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer()); + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual QWidget* createEditor(QWidget* parent = NULL) const; private: @@ -385,8 +388,6 @@ private: const QMetaObject* _metaObject; }; -Q_DECLARE_METATYPE(const QMetaObject*) - /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 9fed9871f6..14db6c66db 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include "AttributeRegistry.h" @@ -21,6 +22,10 @@ REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) +// meta-objects don't quite work with our macro +static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId(), + new SimpleTypeStreamer()); + IDStreamer::IDStreamer(Bitstream& stream) : _stream(stream), _bits(1) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index a564c7a6f1..e0e4faaeee 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -295,6 +295,8 @@ template inline Bitstream& Bitstream::operator>>(QList& list) { return *this; } +Q_DECLARE_METATYPE(const QMetaObject*) + /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index dba34465aa..f34323c072 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -6,6 +6,7 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include #include #include @@ -522,6 +523,17 @@ ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(1.0) { void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { DefaultMetavoxelGuide::guide(visitation); + AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute(); + for (int i = 0; i < visitation.info.inputValues.size(); i++) { + AttributeValue& attributeValue = visitation.info.inputValues[i]; + if (attributeValue.getAttribute() == colorAttribute) { + QRgb base = attributeValue.getInlineValue(); + double seconds = QDateTime::currentMSecsSinceEpoch() / 1000.0; + double amplitude = sin(_rate * seconds) * 0.5 + 0.5; + attributeValue.setInlineValue(qRgba(qRed(base) * amplitude, qGreen(base) * amplitude, + qBlue(base) * amplitude, qAlpha(base))); + } + } } static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 2c20d6fda0..5e554ea46e 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -59,8 +59,8 @@ QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const { } static QItemEditorFactory* getItemEditorFactory() { - static DelegatingItemEditorFactory factory; - return &factory; + static QItemEditorFactory* factory = new DelegatingItemEditorFactory(); + return factory; } static QItemEditorCreatorBase* createDoubleEditorCreator() { From cfbe4de6470c7c5a86d14bb61b53985f44426b8f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 3 Feb 2014 19:46:22 -0800 Subject: [PATCH 10/24] Progress on shared object attributes; basic "throbbing" guide working. --- .../metavoxels/src/AttributeRegistry.cpp | 224 ++++++++++++------ libraries/metavoxels/src/AttributeRegistry.h | 170 ++++++------- libraries/metavoxels/src/Bitstream.cpp | 10 +- libraries/metavoxels/src/MetavoxelData.cpp | 11 +- libraries/metavoxels/src/MetavoxelData.h | 4 +- 5 files changed, 237 insertions(+), 182 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index d4995109fb..9c654514a1 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -19,6 +19,7 @@ #include "MetavoxelData.h" REGISTER_META_OBJECT(QRgbAttribute) +REGISTER_META_OBJECT(SharedObjectAttribute) AttributeRegistry* AttributeRegistry::getInstance() { static AttributeRegistry registry; @@ -27,7 +28,7 @@ AttributeRegistry* AttributeRegistry::getInstance() { AttributeRegistry::AttributeRegistry() : _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, - PolymorphicDataPointer(new DefaultMetavoxelGuide())))), + SharedObjectPointer(new DefaultMetavoxelGuide())))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { } @@ -89,6 +90,10 @@ OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) : AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) { } +OwnedAttributeValue::OwnedAttributeValue(const OwnedAttributeValue& other) : + AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) { +} + OwnedAttributeValue::~OwnedAttributeValue() { if (_attribute) { _attribute->destroy(_value); @@ -105,6 +110,16 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) return *this; } +OwnedAttributeValue& OwnedAttributeValue::operator=(const OwnedAttributeValue& other) { + if (_attribute) { + _attribute->destroy(_value); + } + if ((_attribute = other.getAttribute())) { + _value = other.copy(); + } + return *this; +} + Attribute::Attribute(const QString& name) { setObjectName(name); } @@ -177,33 +192,144 @@ void QRgbEditor::selectColor() { } } -PolymorphicData::~PolymorphicData() { +SharedObject::SharedObject() : _referenceCount(0) { } -template<> PolymorphicData* QExplicitlySharedDataPointer::clone() { - return d->clone(); +void SharedObject::incrementReferenceCount() { + _referenceCount++; } -PolymorphicAttribute::PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue) : - InlineAttribute(name, defaultValue) { +void SharedObject::decrementReferenceCount() { + if (--_referenceCount == 0) { + delete this; + } } -void* PolymorphicAttribute::createFromVariant(const QVariant& value) const { - return create(encodeInline(value.value())); +SharedObject* SharedObject::clone() 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()); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored()) { + property.write(newObject, property.read(this)); + } + } + foreach (const QByteArray& propertyName, dynamicPropertyNames()) { + newObject->setProperty(propertyName, property(propertyName)); + } + return newObject; } -bool PolymorphicAttribute::equal(void* first, void* second) const { - PolymorphicDataPointer firstPointer = decodeInline(first); - PolymorphicDataPointer secondPointer = decodeInline(second); +bool SharedObject::equals(const SharedObject* other) const { + // default behavior is to compare the properties + const QMetaObject* metaObject = this->metaObject(); + if (metaObject != other->metaObject()) { + return false; + } + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored() && property.read(this) != property.read(other)) { + return false; + } + } + QList dynamicPropertyNames = this->dynamicPropertyNames(); + if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) { + return false; + } + foreach (const QByteArray& propertyName, dynamicPropertyNames) { + if (property(propertyName) != other->property(propertyName)) { + return false; + } + } + return true; +} + +SharedObjectPointer::SharedObjectPointer(SharedObject* data) : _data(data) { + if (_data) { + _data->incrementReferenceCount(); + } +} + +SharedObjectPointer::SharedObjectPointer(const SharedObjectPointer& other) : _data(other._data) { + if (_data) { + _data->incrementReferenceCount(); + } +} + +SharedObjectPointer::~SharedObjectPointer() { + if (_data) { + _data->decrementReferenceCount(); + } +} + +void SharedObjectPointer::detach() { + if (_data && _data->getReferenceCount() > 1) { + _data->decrementReferenceCount(); + (_data = _data->clone())->incrementReferenceCount(); + } +} + +void SharedObjectPointer::reset() { + if (_data) { + _data->decrementReferenceCount(); + } + _data = NULL; +} + +SharedObjectPointer& SharedObjectPointer::operator=(SharedObject* data) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +SharedObjectPointer& SharedObjectPointer::operator=(const SharedObjectPointer& other) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = other._data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, + const SharedObjectPointer& defaultValue) : + InlineAttribute(name, defaultValue), + _metaObject(metaObject) { + +} + +void SharedObjectAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + QObject* object; + in >> object; + *((SharedObjectPointer*)&value) = static_cast(object); + } +} + +void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + out << decodeInline(value).data(); + } +} + +bool SharedObjectAttribute::equal(void* first, void* second) const { + SharedObjectPointer firstPointer = decodeInline(first); + SharedObjectPointer secondPointer = decodeInline(second); return (firstPointer == secondPointer) || (firstPointer && secondPointer && firstPointer->equals(secondPointer.constData())); } -bool PolymorphicAttribute::merge(void*& parent, void* children[]) const { - PolymorphicDataPointer& parentPointer = *(PolymorphicDataPointer*)&parent; - PolymorphicDataPointer childPointers[MERGE_COUNT]; +bool SharedObjectAttribute::merge(void*& parent, void* children[]) const { + SharedObjectPointer& parentPointer = *(SharedObjectPointer*)&parent; + SharedObjectPointer childPointers[MERGE_COUNT]; for (int i = 0; i < MERGE_COUNT; i++) { - childPointers[i] = decodeInline(children[i]); + childPointers[i] = decodeInline(children[i]); } if (childPointers[0]) { for (int i = 1; i < MERGE_COUNT; i++) { @@ -227,66 +353,8 @@ bool PolymorphicAttribute::merge(void*& parent, void* children[]) const { return true; } -PolymorphicData* SharedObject::clone() const { - // default behavior is to make a copy using the no-arg constructor and copy the stored properties - const QMetaObject* metaObject = this->metaObject(); - QObject* newObject = metaObject->newInstance(); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored()) { - property.write(newObject, property.read(this)); - } - } - foreach (const QByteArray& propertyName, dynamicPropertyNames()) { - newObject->setProperty(propertyName, property(propertyName)); - } - return static_cast(newObject); -} - -bool SharedObject::equals(const PolymorphicData* other) const { - // default behavior is to compare the properties - const QMetaObject* metaObject = this->metaObject(); - const SharedObject* sharedOther = static_cast(other); - if (metaObject != sharedOther->metaObject()) { - return false; - } - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored() && property.read(this) != property.read(sharedOther)) { - return false; - } - } - QList dynamicPropertyNames = this->dynamicPropertyNames(); - if (dynamicPropertyNames.size() != sharedOther->dynamicPropertyNames().size()) { - return false; - } - foreach (const QByteArray& propertyName, dynamicPropertyNames) { - if (property(propertyName) != sharedOther->property(propertyName)) { - return false; - } - } - return true; -} - -SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, - const PolymorphicDataPointer& defaultValue) : - PolymorphicAttribute(name, defaultValue), - _metaObject(metaObject) { - -} - -void SharedObjectAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (isLeaf) { - QObject* object; - in >> object; - *((PolymorphicDataPointer*)&value) = static_cast(object); - } -} - -void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (isLeaf) { - out << static_cast(decodeInline(value).data()); - } +void* SharedObjectAttribute::createFromVariant(const QVariant& value) const { + return create(encodeInline(value.value())); } QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { @@ -311,9 +379,9 @@ SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* p connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); } -void SharedObjectEditor::setObject(const PolymorphicDataPointer& object) { +void SharedObjectEditor::setObject(const SharedObjectPointer& object) { _object = object; - const QMetaObject* metaObject = object ? static_cast(object.data())->metaObject() : NULL; + const QMetaObject* metaObject = object ? object->metaObject() : NULL; int index = _type->findData(QVariant::fromValue(metaObject)); if (index != -1) { // ensure that we call updateType to obtain the values @@ -388,7 +456,7 @@ void SharedObjectEditor::propertyChanged() { continue; } _object.detach(); - QObject* object = static_cast(_object.data()); + QObject* object = _object.data(); QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); property.write(object, widget->property(valuePropertyName)); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 37c17934b3..6442f82d0c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -129,11 +129,17 @@ public: /// Creates an owned attribute with a copy of the specified other value. OwnedAttributeValue(const AttributeValue& other); + /// Creates an owned attribute with a copy of the specified other value. + OwnedAttributeValue(const OwnedAttributeValue& other); + /// Destroys the current value, if any. ~OwnedAttributeValue(); /// Destroys the current value, if any, and copies the specified other value. OwnedAttributeValue& operator=(const AttributeValue& other); + + /// Destroys the current value, if any, and copies the specified other value. + OwnedAttributeValue& operator=(const OwnedAttributeValue& other); }; /// Represents a registered attribute. @@ -272,115 +278,91 @@ private: QColor _color; }; -/// An attribute class that stores pointers to its values. -template class PointerAttribute : public Attribute { -public: - - PointerAttribute(const QString& name, T defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { } - - virtual void* create(void* copy) const { new T(*static_cast(copy)); } - virtual void destroy(void* value) const { delete static_cast(value); } - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual bool equal(void* first, void* second) const { return *static_cast(first) == *static_cast(second); } - - virtual void* getDefaultValue() const { return const_cast((void*)&_defaultValue); } - -private: - - T _defaultValue; -}; - -template inline void PointerAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (isLeaf) { - in.read(value, sizeof(T) * 8); - } -} - -template inline void PointerAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (isLeaf) { - out.write(value, sizeof(T) * 8); - } -} - -/// Provides merging using the =, ==, += and /= operators. -template class SimplePointerAttribute : public PointerAttribute { -public: - - SimplePointerAttribute(const QString& name, T defaultValue = T()) : PointerAttribute(name, defaultValue) { } - - virtual bool merge(void*& parent, void* children[]) const; -}; - -template inline bool SimplePointerAttribute::merge(void*& parent, void* children[]) const { - T& merged = *static_cast(parent); - merged = *static_cast(children[0]); - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - merged += *static_cast(children[i]); - allChildrenEqual &= (*static_cast(children[0]) == *static_cast(children[i])); - } - merged /= Attribute::MERGE_COUNT; - return allChildrenEqual; -} - -/// Base class for polymorphic attribute data. -class PolymorphicData : public QSharedData { -public: - - virtual ~PolymorphicData(); - - /// Creates a new clone of this object. - virtual PolymorphicData* clone() const = 0; - - /// Tests this object for equality with another. - virtual bool equals(const PolymorphicData* other) const = 0; -}; - -template<> PolymorphicData* QExplicitlySharedDataPointer::clone(); - -typedef QExplicitlySharedDataPointer PolymorphicDataPointer; - -Q_DECLARE_METATYPE(PolymorphicDataPointer) - -/// Provides polymorphic streaming and averaging. -class PolymorphicAttribute : public InlineAttribute { -public: - - PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer()); - - virtual void* createFromVariant(const QVariant& value) const; - - virtual bool equal(void* first, void* second) const; - - virtual bool merge(void*& parent, void* children[]) const; -}; - -class SharedObject : public QObject, public PolymorphicData { +/// A QObject with a reference count. +class SharedObject : public QObject { Q_OBJECT public: - virtual PolymorphicData* clone() const; + SharedObject(); + + int getReferenceCount() const { return _referenceCount; } + void incrementReferenceCount(); + void decrementReferenceCount(); + + /// Creates a new clone of this object. + virtual SharedObject* clone() const; + + /// Tests this object for equality with another. + virtual bool equals(const SharedObject* other) const; + +private: - virtual bool equals(const PolymorphicData* other) const; + int _referenceCount; }; +/// A pointer to a shared object. +class SharedObjectPointer { +public: + + SharedObjectPointer(SharedObject* data = NULL); + SharedObjectPointer(const SharedObjectPointer& other); + ~SharedObjectPointer(); + + SharedObject* data() { return _data; } + const SharedObject* data() const { return _data; } + const SharedObject* constData() const { return _data; } + + void detach(); + + void swap(SharedObjectPointer& other) { qSwap(_data, other._data); } + + void reset(); + + operator SharedObject*() { return _data; } + operator const SharedObject*() const { return _data; } + + bool operator!() const { return !_data; } + + bool operator!=(const SharedObjectPointer& other) const { return _data != other._data; } + + SharedObject& operator*() { return *_data; } + const SharedObject& operator*() const { return *_data; } + + SharedObject* operator->() { return _data; } + const SharedObject* operator->() const { return _data; } + + SharedObjectPointer& operator=(SharedObject* data); + SharedObjectPointer& operator=(const SharedObjectPointer& other); + + bool operator==(const SharedObjectPointer& other) const { return _data == other._data; } + +private: + + SharedObject* _data; +}; + +Q_DECLARE_METATYPE(SharedObjectPointer) + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). -class SharedObjectAttribute : public PolymorphicAttribute { +class SharedObjectAttribute : public InlineAttribute { Q_OBJECT Q_PROPERTY(const QMetaObject* metaObject MEMBER _metaObject) public: Q_INVOKABLE SharedObjectAttribute(const QString& name = QString(), const QMetaObject* metaObject = NULL, - const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer()); + const SharedObjectPointer& defaultValue = SharedObjectPointer()); virtual void read(Bitstream& in, void*& value, bool isLeaf) const; virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual bool equal(void* first, void* second) const; + + virtual bool merge(void*& parent, void* children[]) const; + + virtual void* createFromVariant(const QVariant& value) const; + virtual QWidget* createEditor(QWidget* parent = NULL) const; private: @@ -391,7 +373,7 @@ private: /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT - Q_PROPERTY(PolymorphicDataPointer object MEMBER _object WRITE setObject USER true) + Q_PROPERTY(SharedObjectPointer object MEMBER _object WRITE setObject USER true) public: @@ -399,7 +381,7 @@ public: public slots: - void setObject(const PolymorphicDataPointer& object); + void setObject(const SharedObjectPointer& object); private slots: @@ -409,7 +391,7 @@ private slots: private: QComboBox* _type; - PolymorphicDataPointer _object; + SharedObjectPointer _object; }; #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 14db6c66db..e6be8b661f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -13,16 +13,20 @@ #include #include +#include + #include "AttributeRegistry.h" #include "Bitstream.h" +REGISTER_SIMPLE_TYPE_STREAMER(bool) +REGISTER_SIMPLE_TYPE_STREAMER(int) +REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QString) REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) -REGISTER_SIMPLE_TYPE_STREAMER(bool) -REGISTER_SIMPLE_TYPE_STREAMER(int) -// meta-objects don't quite work with our macro +// some types don't quite work with our macro +static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index f34323c072..f04de110c6 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -13,6 +13,7 @@ #include "MetavoxelData.h" #include "MetavoxelUtil.h" +REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) @@ -65,7 +66,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { firstVisitation.outputNodes[i] = node; } static_cast(firstVisitation.info.inputValues.last().getInlineValue< - PolymorphicDataPointer>().data())->guide(firstVisitation); + SharedObjectPointer>().data())->guide(firstVisitation); for (int i = 0; i < outputs.size(); i++) { AttributeValue& value = firstVisitation.info.outputValues[i]; if (!value.getAttribute()) { @@ -473,7 +474,7 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f); static_cast(nextVisitation.info.inputValues.last().getInlineValue< - PolymorphicDataPointer>().data())->guide(nextVisitation); + SharedObjectPointer>().data())->guide(nextVisitation); for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { AttributeValue& value = nextVisitation.info.outputValues[j]; if (!value.getAttribute()) { @@ -517,12 +518,10 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } -ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(1.0) { +ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(10.0) { } void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - DefaultMetavoxelGuide::guide(visitation); - AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute(); for (int i = 0; i < visitation.info.inputValues.size(); i++) { AttributeValue& attributeValue = visitation.info.inputValues[i]; @@ -534,6 +533,8 @@ void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { qBlue(base) * amplitude, qAlpha(base))); } } + + DefaultMetavoxelGuide::guide(visitation); } static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 658f83f38f..7a92a65b81 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -173,7 +173,7 @@ public: /// A temporary test guide that just makes the existing voxels throb with delight. class ThrobbingMetavoxelGuide : public DefaultMetavoxelGuide { Q_OBJECT - Q_PROPERTY(double rate MEMBER _rate) + Q_PROPERTY(float rate MEMBER _rate) public: @@ -183,7 +183,7 @@ public: private: - double _rate; + float _rate; }; /// Represents a guide implemented in Javascript. From fd31cc02ad61d9befaf5c9a67b36d862587e1af0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Feb 2014 14:06:17 -0800 Subject: [PATCH 11/24] Working on loading scripts. --- libraries/metavoxels/CMakeLists.txt | 3 +- libraries/metavoxels/src/Bitstream.cpp | 14 ++++++++++ libraries/metavoxels/src/Bitstream.h | 31 +++++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 1 + libraries/metavoxels/src/MetavoxelUtil.cpp | 32 ++++++++++++++++++++++ libraries/metavoxels/src/MetavoxelUtil.h | 19 +++++++++++-- 6 files changed, 97 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 98b2baf7ac..989ed6d4a7 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm set(TARGET_NAME metavoxels) +find_package(Qt5Network REQUIRED) find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) @@ -16,7 +17,7 @@ setup_hifi_library(${TARGET_NAME}) include(${MACRO_DIR}/AutoMTC.cmake) auto_mtc(${TARGET_NAME} ${ROOT_DIR}) -qt5_use_modules(${TARGET_NAME} Widgets Script) +qt5_use_modules(${TARGET_NAME} Network Script Widgets) include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} ${ROOT_DIR}) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e6be8b661f..686508a973 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,9 @@ REGISTER_SIMPLE_TYPE_STREAMER(int) REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QString) +REGISTER_SIMPLE_TYPE_STREAMER(QUrl) REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) +REGISTER_SIMPLE_TYPE_STREAMER(QVariantHash) // some types don't quite work with our macro static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); @@ -235,6 +238,17 @@ Bitstream& Bitstream::operator>>(QString& string) { return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); } +Bitstream& Bitstream::operator<<(const QUrl& url) { + return *this << url.toString(); +} + +Bitstream& Bitstream::operator>>(QUrl& url) { + QString string; + *this >> string; + url = string; + return *this; +} + Bitstream& Bitstream::operator<<(const QVariant& value) { const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index e0e4faaeee..0605cde50b 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -21,6 +21,7 @@ class QByteArray; class QDataStream; struct QMetaObject; class QObject; +class QUrl; class Attribute; class AttributeValue; @@ -238,6 +239,9 @@ public: Bitstream& operator<<(const QString& string); Bitstream& operator>>(QString& string); + Bitstream& operator<<(const QUrl& url); + Bitstream& operator>>(QUrl& url); + Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); @@ -247,6 +251,9 @@ public: template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); + template Bitstream& operator<<(const QHash& hash); + template Bitstream& operator>>(QHash& hash); + Bitstream& operator<<(const QObject* object); Bitstream& operator>>(QObject*& object); @@ -295,6 +302,30 @@ template inline Bitstream& Bitstream::operator>>(QList& list) { return *this; } +template inline Bitstream& Bitstream::operator<<(const QHash& hash) { + *this << hash.size(); + for (typename QHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); it++) { + *this << it.key(); + *this << it.value(); + } + return *this; +} + +template inline Bitstream& Bitstream::operator>>(QHash& hash) { + int size; + *this >> size; + hash.clear(); + hash.reserve(size); + for (int i = 0; i < size; i++) { + K key; + V value; + *this >> key; + *this >> value; + hash.insertMulti(key, value); + } + return *this; +} + Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index f04de110c6..98ae50e6c3 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -15,6 +15,7 @@ REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) +REGISTER_META_OBJECT(ScriptedMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) MetavoxelData::MetavoxelData() : _size(1.0f) { diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 5e554ea46e..08c41ec114 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -19,6 +21,8 @@ #include "MetavoxelUtil.h" +REGISTER_SIMPLE_TYPE_STREAMER(ParameterizedURL) + class DelegatingItemEditorFactory : public QItemEditorFactory { public: @@ -112,15 +116,43 @@ bool ParameterizedURL::operator!=(const ParameterizedURL& other) const { return _url != other._url || _parameters != other._parameters; } +uint qHash(const ParameterizedURL& url, uint seed) { + // just hash on the URL, for now + return qHash(url.getURL(), seed); +} + +Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url) { + out << url.getURL(); + out << url.getParameters(); + return out; +} + +Bitstream& operator>>(Bitstream& in, ParameterizedURL& url) { + QUrl qurl; + in >> qurl; + QVariantHash parameters; + in >> parameters; + url = ParameterizedURL(qurl, parameters); + return in; +} + ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); + layout->setContentsMargins(QMargins()); setLayout(layout); layout->addWidget(_line = new QLineEdit()); + connect(_line, SIGNAL(textChanged(const QString&)), SLOT(updateURL())); } void ParameterizedURLEditor::setURL(const ParameterizedURL& url) { _url = url; + _line->setText(url.getURL().toString()); +} + +void ParameterizedURLEditor::updateURL() { + _url = ParameterizedURL(_line->text()); + emit urlChanged(_url); } diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 27cd1b5deb..1ae77766f7 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -45,6 +45,8 @@ public: ParameterizedURL(const QUrl& url = QUrl(), const QVariantHash& parameters = QVariantHash()); + bool isValid() const { return _url.isValid(); } + void setURL(const QUrl& url) { _url = url; } const QUrl& getURL() const { return _url; } @@ -60,21 +62,34 @@ private: QVariantHash _parameters; }; +uint qHash(const ParameterizedURL& url, uint seed = 0); + +Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url); +Bitstream& operator>>(Bitstream& in, ParameterizedURL& url); + Q_DECLARE_METATYPE(ParameterizedURL) /// Allows editing parameterized URLs. class ParameterizedURLEditor : public QWidget { Q_OBJECT - Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL USER true) + Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL NOTIFY urlChanged USER true) public: ParameterizedURLEditor(QWidget* parent = NULL); - + +signals: + + void urlChanged(const ParameterizedURL& url); + public slots: void setURL(const ParameterizedURL& url); +private slots: + + void updateURL(); + private: ParameterizedURL _url; From 3af86cb1a40fcfe3622e008c0ddef70203f99aa4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Feb 2014 14:07:01 -0800 Subject: [PATCH 12/24] Working on script loading. --- libraries/metavoxels/src/ScriptCache.cpp | 124 +++++++++++++++++++++++ libraries/metavoxels/src/ScriptCache.h | 104 +++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 libraries/metavoxels/src/ScriptCache.cpp create mode 100644 libraries/metavoxels/src/ScriptCache.h diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp new file mode 100644 index 0000000000..6f951b4451 --- /dev/null +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -0,0 +1,124 @@ +// +// ScriptCache.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 2/4/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include + +#include +#include +#include +#include +#include + +#include "MetavoxelUtil.h" +#include "ScriptCache.h" + +static ScriptCache* ScriptCache::getInstance() { + static ScriptCache cache; + return &cache; +} + +ScriptCache::ScriptCache() : + _networkAccessManager(new QNetworkAccessManager(this)), + _engine(new QScriptEngine(this)) { +} + +void ScriptCache::setNetworkAccessManager(QNetworkAccessManager* manager) { + if (_networkAccessManager->parent() == this) { + delete _networkAccessManager; + } + _networkAccessManager = manager; +} + +void ScriptCache::setEngine(QScriptEngine* engine) { + if (_engine->parent() == this) { + delete _engine; + } + _engine = engine; +} + +QSharedPointer ScriptCache::getProgram(const QUrl& url) { + QSharedPointer program = _networkPrograms.value(url); + if (program.isNull()) { + program = QSharedPointer(new NetworkProgram(this, url)); + _networkPrograms.insert(url, program); + } + return program; +} + +QSharedPointer ScriptCache::getValue(const ParameterizedURL& url) { + QSharedPointer value = _networkValues.value(url); + if (value.isNull()) { + value = QSharedPointer(new NetworkValue(getProgram(url.getURL()), url.getParameters())); + _networkValues.insert(url, value); + } + return value; +} + +NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) : + _cache(cache), + _request(url), + _reply(NULL), + _attempts(0) { + + if (!url.isValid()) { + return; + } + _request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + makeRequest(); +} + +NetworkProgram::~NetworkProgram() { + if (_reply != NULL) { + delete _reply; + } +} + +void NetworkProgram::makeRequest() { + _reply = _cache->getNetworkAccessManager()->get(_request); + + connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); + connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); +} + +void NetworkProgram::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + if (bytesReceived < bytesTotal && !_reply->isFinished()) { + return; + } + _program = QScriptProgram(QTextStream(_reply).readAll(), _reply->url().toString()); + _reply->disconnect(this); + _reply->deleteLater(); + _reply = NULL; +} + +void NetworkProgram::handleReplyError() { + QDebug debug = qDebug() << _reply->errorString(); + + _reply->disconnect(this); + _reply->deleteLater(); + _reply = NULL; + + // retry with increasing delays + const int MAX_ATTEMPTS = 8; + const int BASE_DELAY_MS = 1000; + if (++_attempts < MAX_ATTEMPTS) { + QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest())); + debug << " -- retrying..."; + } +} + +NetworkValue::NetworkValue(const QSharedPointer& program, const QVariantHash& parameters) : + _program(program), + _parameters(parameters) { +} + +const QScriptValue& NetworkValue::getValue() { + if (!_value.isValid() && _program->isLoaded()) { + _value = _program->getCache()->getEngine()->evaluate(_program->getProgram()); + } + return _value; +} diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h new file mode 100644 index 0000000000..2670e7f4ef --- /dev/null +++ b/libraries/metavoxels/src/ScriptCache.h @@ -0,0 +1,104 @@ +// +// ScriptCache.h +// metavoxels +// +// Created by Andrzej Kapolka on 2/4/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__ScriptCache__ +#define __interface__ScriptCache__ + +#include +#include +#include +#include +#include +#include +#include + +class QNetworkAccessManager; +class QNetworkReply; +class QScriptEngine; +class QUrl; + +class NetworkProgram; +class NetworkValue; +class ParameterizedURL; + +/// Maintains a cache of loaded scripts. +class ScriptCache : public QObject { + Q_OBJECT + +public: + + static ScriptCache* getInstance(); + + ScriptCache(); + + void setNetworkAccessManager(QNetworkAccessManager* manager); + QNetworkAccessManager* getNetworkAccessManager() const { return _networkAccessManager; } + + void setEngine(QScriptEngine* engine); + QScriptEngine* getEngine() const { return _engine; } + + /// Loads a script program from the specified URL. + QSharedPointer getProgram(const QUrl& url); + + /// Loads a script value from the specified URL. + QSharedPointer getValue(const ParameterizedURL& url); + +private: + + QNetworkAccessManager* _networkAccessManager; + QScriptEngine* _engine; + QHash > _networkPrograms; + QHash > _networkValues; +}; + +/// A program loaded from the network. +class NetworkProgram : public QObject { + Q_OBJECT + +public: + + NetworkProgram(ScriptCache* cache, const QUrl& url); + ~NetworkProgram(); + + ScriptCache* getCache() const { return _cache; } + + bool isLoaded() const { return !_program.isNull(); } + + const QScriptProgram& getProgram() const { return _program; } + +private slots: + + void makeRequest(); + void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void handleReplyError(); + +private: + + ScriptCache* _cache; + QNetworkRequest _request; + QNetworkReply* _reply; + int _attempts; + QScriptProgram _program; +}; + +/// A value loaded from the network. +class NetworkValue { +public: + + NetworkValue(const QSharedPointer& program, const QVariantHash& parameters); + + const QScriptValue& getValue(); + +private: + + QSharedPointer _program; + QVariantHash _parameters; + QScriptValue _value; +}; + +#endif /* defined(__interface__ScriptCache__) */ From ca52d67041bd8465282cb972afbc470aacbca002 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Feb 2014 15:40:17 -0800 Subject: [PATCH 13/24] Basic script guides now working. --- interface/src/MetavoxelSystem.cpp | 4 ++ libraries/metavoxels/src/MetavoxelData.cpp | 78 +++++++++++++--------- libraries/metavoxels/src/MetavoxelData.h | 15 +++-- libraries/metavoxels/src/MetavoxelUtil.cpp | 3 + libraries/metavoxels/src/ScriptCache.cpp | 23 ++++--- libraries/metavoxels/src/ScriptCache.h | 7 +- 6 files changed, 83 insertions(+), 47 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 311c8f2556..22af29cfcf 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "Application.h" #include "MetavoxelSystem.h" @@ -37,6 +38,9 @@ void MetavoxelSystem::init() { _program.link(); _pointScaleLocation = _program.uniformLocation("pointScale"); + + // let the script cache know to use our common access manager + ScriptCache::getInstance()->setNetworkAccessManager(Application::getInstance()->getNetworkAccessManager()); } NodeList* nodeList = NodeList::getInstance(); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 98ae50e6c3..951378979b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -12,6 +12,7 @@ #include "MetavoxelData.h" #include "MetavoxelUtil.h" +#include "ScriptCache.h" REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) @@ -593,36 +594,47 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin return result; } -ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction) : - _guideFunction(guideFunction) { - - QScriptEngine* engine = guideFunction.engine(); - if (!engine) { - return; - } - _minimumHandle = engine->toStringHandle("minimum"); - _sizeHandle = engine->toStringHandle("size"); - _inputValuesHandle = engine->toStringHandle("inputValues"); - _outputValuesHandle = engine->toStringHandle("outputValues"); - _isLeafHandle = engine->toStringHandle("isLeaf"); - _getInputsFunction = engine->newFunction(getInputs, 0); - _getOutputsFunction = engine->newFunction(getOutputs, 0); - _visitFunction = engine->newFunction(visit, 1); - _info = engine->newObject(); - _minimum = engine->newArray(3); - - _arguments.append(engine->newObject()); - QScriptValue visitor = engine->newObject(); - visitor.setProperty("getInputs", _getInputsFunction); - visitor.setProperty("getOutputs", _getOutputsFunction); - visitor.setProperty("visit", _visitFunction); - _arguments[0].setProperty("visitor", visitor); - _arguments[0].setProperty("info", _info); - _info.setProperty(_minimumHandle, _minimum); +ScriptedMetavoxelGuide::ScriptedMetavoxelGuide() { } void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue(this)); + QScriptValue guideFunction; + if (_guideFunction) { + guideFunction = _guideFunction->getValue(); + + } else if (_url.isValid()) { + _guideFunction = ScriptCache::getInstance()->getValue(_url); + guideFunction = _guideFunction->getValue(); + } + if (!guideFunction.isValid()) { + // before we load, just use the default behavior + DefaultMetavoxelGuide::guide(visitation); + return; + } + QScriptEngine* engine = guideFunction.engine(); + if (!_minimumHandle.isValid()) { + _minimumHandle = engine->toStringHandle("minimum"); + _sizeHandle = engine->toStringHandle("size"); + _inputValuesHandle = engine->toStringHandle("inputValues"); + _outputValuesHandle = engine->toStringHandle("outputValues"); + _isLeafHandle = engine->toStringHandle("isLeaf"); + _getInputsFunction = engine->newFunction(getInputs, 0); + _getOutputsFunction = engine->newFunction(getOutputs, 0); + _visitFunction = engine->newFunction(visit, 1); + _info = engine->newObject(); + _minimum = engine->newArray(3); + + _arguments.clear(); + _arguments.append(engine->newObject()); + QScriptValue visitor = engine->newObject(); + visitor.setProperty("getInputs", _getInputsFunction); + visitor.setProperty("getOutputs", _getOutputsFunction); + visitor.setProperty("visit", _visitFunction); + _arguments[0].setProperty("visitor", visitor); + _arguments[0].setProperty("info", _info); + _info.setProperty(_minimumHandle, _minimum); + } + QScriptValue data = engine->newVariant(QVariant::fromValue(this)); _getInputsFunction.setData(data); _visitFunction.setData(data); _minimum.setProperty(0, visitation.info.minimum.x); @@ -631,12 +643,18 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { _info.setProperty(_sizeHandle, visitation.info.size); _info.setProperty(_isLeafHandle, visitation.info.isLeaf); _visitation = &visitation; - _guideFunction.call(QScriptValue(), _arguments); - if (_guideFunction.engine()->hasUncaughtException()) { - qDebug() << "Script error: " << _guideFunction.engine()->uncaughtException().toString(); + guideFunction.call(QScriptValue(), _arguments); + if (engine->hasUncaughtException()) { + qDebug() << "Script error: " << engine->uncaughtException().toString(); } } +void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) { + _url = url; + _guideFunction.reset(); + _minimumHandle = QScriptString(); +} + bool MetavoxelVisitation::allInputNodesLeaves() const { foreach (MetavoxelNode* node, inputNodes) { if (node != NULL && !node->isLeaf()) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 7a92a65b81..51cdd6cf64 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ class QScriptContext; class MetavoxelNode; class MetavoxelVisitation; class MetavoxelVisitor; +class NetworkValue; /// The base metavoxel representation shared between server and client. class MetavoxelData { @@ -187,16 +189,20 @@ private: }; /// Represents a guide implemented in Javascript. -class ScriptedMetavoxelGuide : public MetavoxelGuide { +class ScriptedMetavoxelGuide : public DefaultMetavoxelGuide { Q_OBJECT - Q_PROPERTY(ParameterizedURL url MEMBER _url) + Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL) public: - Q_INVOKABLE ScriptedMetavoxelGuide(const QScriptValue& guideFunction = QScriptValue()); + Q_INVOKABLE ScriptedMetavoxelGuide(); virtual void guide(MetavoxelVisitation& visitation); +public slots: + + void setURL(const ParameterizedURL& url); + private: static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine); @@ -205,7 +211,8 @@ private: ParameterizedURL _url; - QScriptValue _guideFunction; + QSharedPointer _guideFunction; + QScriptString _minimumHandle; QScriptString _sizeHandle; QScriptString _inputValuesHandle; diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 08c41ec114..1620226386 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,8 @@ #include "MetavoxelUtil.h" +static int parameterizedURLType = qRegisterMetaType(); + REGISTER_SIMPLE_TYPE_STREAMER(ParameterizedURL) class DelegatingItemEditorFactory : public QItemEditorFactory { diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 6f951b4451..18be6a1dea 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -14,24 +14,20 @@ #include #include +#include "AttributeRegistry.h" #include "MetavoxelUtil.h" #include "ScriptCache.h" -static ScriptCache* ScriptCache::getInstance() { +ScriptCache* ScriptCache::getInstance() { static ScriptCache cache; return &cache; } ScriptCache::ScriptCache() : - _networkAccessManager(new QNetworkAccessManager(this)), + _networkAccessManager(NULL), _engine(new QScriptEngine(this)) { -} - -void ScriptCache::setNetworkAccessManager(QNetworkAccessManager* manager) { - if (_networkAccessManager->parent() == this) { - delete _networkAccessManager; - } - _networkAccessManager = manager; + + AttributeRegistry::getInstance()->configureScriptEngine(_engine); } void ScriptCache::setEngine(QScriptEngine* engine) { @@ -79,7 +75,11 @@ NetworkProgram::~NetworkProgram() { } void NetworkProgram::makeRequest() { - _reply = _cache->getNetworkAccessManager()->get(_request); + QNetworkAccessManager* manager = _cache->getNetworkAccessManager(); + if (manager == NULL) { + return; + } + _reply = manager->get(_request); connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); @@ -90,6 +90,7 @@ void NetworkProgram::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo return; } _program = QScriptProgram(QTextStream(_reply).readAll(), _reply->url().toString()); + _reply->disconnect(this); _reply->deleteLater(); _reply = NULL; @@ -116,7 +117,7 @@ NetworkValue::NetworkValue(const QSharedPointer& program, const _parameters(parameters) { } -const QScriptValue& NetworkValue::getValue() { +QScriptValue& NetworkValue::getValue() { if (!_value.isValid() && _program->isLoaded()) { _value = _program->getCache()->getEngine()->evaluate(_program->getProgram()); } diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index 2670e7f4ef..6d4561d669 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ public: ScriptCache(); - void setNetworkAccessManager(QNetworkAccessManager* manager); + void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; } QNetworkAccessManager* getNetworkAccessManager() const { return _networkAccessManager; } void setEngine(QScriptEngine* engine); @@ -92,7 +93,9 @@ public: NetworkValue(const QSharedPointer& program, const QVariantHash& parameters); - const QScriptValue& getValue(); + bool isLoaded() { return getValue().isValid(); } + + QScriptValue& getValue(); private: From 4f4c4c68d9ca1ddac431e7a405b1dbd8057e9d29 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Feb 2014 18:31:18 -0800 Subject: [PATCH 14/24] Streaming cleanup, streaming script strings as repeated values. --- interface/resources/scripts/sphere.js | 171 --------------------- libraries/metavoxels/src/Bitstream.cpp | 75 +++++++-- libraries/metavoxels/src/Bitstream.h | 26 +++- libraries/metavoxels/src/MetavoxelData.cpp | 12 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 40 ++++- libraries/metavoxels/src/MetavoxelUtil.h | 18 ++- libraries/metavoxels/src/ScriptCache.cpp | 29 +++- libraries/metavoxels/src/ScriptCache.h | 49 ++++-- 8 files changed, 206 insertions(+), 214 deletions(-) delete mode 100644 interface/resources/scripts/sphere.js diff --git a/interface/resources/scripts/sphere.js b/interface/resources/scripts/sphere.js deleted file mode 100644 index b696021fe8..0000000000 --- a/interface/resources/scripts/sphere.js +++ /dev/null @@ -1,171 +0,0 @@ -// -// sphere.js -// interface -// -// Created by Andrzej Kapolka on 12/17/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -function strictIndexOf(array, element) { - for (var i = 0; i < array.length; i++) { - if (array[i] == element) { - return i; - } - } - return -1; -} - -var colorIndex; -var normalIndex; -var visitor; -var info; - -var MAX_DEPTH = 4; - -var sphereCenter = [ 0.5, 0.5, 0.5 ]; -var sphereColor = 0xFFFF00FF; -var sphereRadius = 0.25; -var sphereRadiusSquared = sphereRadius * sphereRadius; - -function lengthSquared(x, y, z) { - return x*x + y*y + z*z; -} - -function setNormal(vector) { - if (normalIndex != -1) { - var length = Math.sqrt(lengthSquared(vector[0], vector[1], vector[2])); - if (length == 0.0) { - info.inputValues[normalIndex] = 0x007F00; - - } else { - var scale = 127.0 / length; - info.inputValues[normalIndex] = - (Math.floor(vector[0] * scale) & 0xFF) << 16 | - (Math.floor(vector[1] * scale) & 0xFF) << 8 | - Math.floor(vector[2] * scale) & 0xFF; - } - } -} - -function guide(minimum, size, depth) { - info.minimum = minimum; - info.size = size; - - // start with a relative fast bounding volume test to find most non-intersecting states - var maximum = [ minimum[0] + size, minimum[1] + size, minimum[2] + size ]; - if (minimum[0] >= sphereCenter[0] + sphereRadius || - minimum[1] >= sphereCenter[1] + sphereRadius || - minimum[2] >= sphereCenter[2] + sphereRadius || - maximum[0] <= sphereCenter[0] - sphereRadius || - maximum[1] <= sphereCenter[1] - sphereRadius || - maximum[2] <= sphereCenter[2] - sphereRadius) { - info.isLeaf = true; - if (colorIndex != -1) { - info.inputValues[colorIndex] = 0x0; - } - visitor.visit(info); - return; - } - - var halfSize = size / 2; - var center = [ minimum[0] + halfSize, minimum[1] + halfSize, minimum[2] + halfSize ]; - var vector = [ center[0] - sphereCenter[0], center[1] - sphereCenter[1], center[2] - sphereCenter[2] ]; - - // count the number of points inside the sphere - var inside = 0; - if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <= - sphereRadiusSquared) { - inside++; - } - if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <= - sphereRadiusSquared) { - inside++; - } - - // see if all points are in the sphere - if (inside == 8) { - info.isLeaf = true; - if (colorIndex != -1) { - info.inputValues[colorIndex] = sphereColor; - } - setNormal(vector); - visitor.visit(info); - return; - } - - // if we've reached max depth, compute alpha using a volume estimate - if (depth == MAX_DEPTH) { - info.isLeaf = true; - if (inside >= 3) { - if (colorIndex != -1) { - info.inputValues[colorIndex] = sphereColor; - } - setNormal(vector); - - } else { - if (colorIndex != -1) { - info.inputValues[colorIndex] = 0x0; - } - } - visitor.visit(info); - return; - } - - // recurse - info.isLeaf = false; - if (!visitor.visit(info)) { - return; - } - depth += 1; - guide(minimum, halfSize, depth); - guide([ center[0], minimum[1], minimum[2] ], halfSize, depth); - guide([ minimum[0], center[1], minimum[2] ], halfSize, depth); - guide([ center[0], center[1], minimum[2] ], halfSize, depth); - guide([ minimum[0], minimum[1], center[2] ], halfSize, depth); - guide([ center[0], minimum[1], center[2] ], halfSize, depth); - guide([ minimum[0], center[1], center[2] ], halfSize, depth); - guide([ center[0], center[1], center[2] ], halfSize, depth); -} - -(function(visitation) { - var inputs = visitation.visitor.getInputs(); - colorIndex = strictIndexOf(inputs, AttributeRegistry.colorAttribute); - normalIndex = strictIndexOf(inputs, AttributeRegistry.normalAttribute); - visitor = visitation.visitor; - info = { inputValues: new Array(inputs.length) }; - - // have the sphere orbit the center and pulse in size - var time = new Date().getTime(); - var ROTATE_PERIOD = 400.0; - sphereCenter[0] = 0.5 + 0.25 * Math.cos(time / ROTATE_PERIOD); - sphereCenter[2] = 0.5 + 0.25 * Math.sin(time / ROTATE_PERIOD); - var PULSE_PERIOD = 300.0; - sphereRadius = 0.25 + 0.0625 * Math.cos(time / PULSE_PERIOD); - sphereRadiusSquared = sphereRadius * sphereRadius; - - guide(visitation.info.minimum, visitation.info.size, 0); -}) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 686508a973..dfc2c9bd75 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -18,6 +18,7 @@ #include "AttributeRegistry.h" #include "Bitstream.h" +#include "ScriptCache.h" REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) @@ -88,7 +89,8 @@ Bitstream::Bitstream(QDataStream& underlying) : _position(0), _metaObjectStreamer(*this), _typeStreamerStreamer(*this), - _attributeStreamer(*this) { + _attributeStreamer(*this), + _scriptStringStreamer(*this) { } const int BITS_IN_BYTE = 8; @@ -145,7 +147,8 @@ void Bitstream::reset() { Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), _typeStreamerStreamer.getAndResetTransientOffsets(), - _attributeStreamer.getAndResetTransientOffsets() }; + _attributeStreamer.getAndResetTransientOffsets(), + _scriptStringStreamer.getAndResetTransientOffsets() }; return mappings; } @@ -153,12 +156,14 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { _metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets); _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); + _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); } Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), _typeStreamerStreamer.getAndResetTransientValues(), - _attributeStreamer.getAndResetTransientValues() }; + _attributeStreamer.getAndResetTransientValues(), + _scriptStringStreamer.getAndResetTransientValues() }; return mappings; } @@ -166,6 +171,7 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { _metaObjectStreamer.persistTransientValues(mappings.metaObjectValues); _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); _attributeStreamer.persistTransientValues(mappings.attributeValues); + _scriptStringStreamer.persistTransientValues(mappings.scriptStringValues); } Bitstream& Bitstream::operator<<(bool value) { @@ -238,6 +244,16 @@ Bitstream& Bitstream::operator>>(QString& string) { return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); } +Bitstream& Bitstream::operator<<(const QScriptString& string) { + _scriptStringStreamer << string; + return *this; +} + +Bitstream& Bitstream::operator>>(QScriptString& string) { + _scriptStringStreamer >> string; + return *this; +} + Bitstream& Bitstream::operator<<(const QUrl& url) { return *this << url.toString(); } @@ -334,11 +350,41 @@ Bitstream& Bitstream::operator>>(QObject*& object) { } Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { - return *this << (metaObject ? QByteArray::fromRawData( - metaObject->className(), strlen(metaObject->className())) : QByteArray()); + _metaObjectStreamer << metaObject; + return *this; } Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { + _metaObjectStreamer >> metaObject; + return *this; +} + +Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { + _typeStreamerStreamer << streamer; + return *this; +} + +Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { + _typeStreamerStreamer >> streamer; + return *this; +} + +Bitstream& Bitstream::operator<<(const AttributePointer& attribute) { + _attributeStreamer << attribute; + return *this; +} + +Bitstream& Bitstream::operator>>(AttributePointer& attribute) { + _attributeStreamer >> attribute; + return *this; +} + +Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { + return *this << (metaObject ? QByteArray::fromRawData(metaObject->className(), + strlen(metaObject->className())) : QByteArray()); +} + +Bitstream& Bitstream::operator>(const QMetaObject*& metaObject) { QByteArray className; *this >> className; if (className.isEmpty()) { @@ -352,12 +398,12 @@ Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { return *this; } -Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { +Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { const char* typeName = QMetaType::typeName(streamer->getType()); return *this << QByteArray::fromRawData(typeName, strlen(typeName)); } -Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { +Bitstream& Bitstream::operator>(const TypeStreamer*& streamer) { QByteArray typeName; *this >> typeName; streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); @@ -367,17 +413,28 @@ Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { return *this; } -Bitstream& Bitstream::operator<<(const AttributePointer& attribute) { +Bitstream& Bitstream::operator<(const AttributePointer& attribute) { return *this << (QObject*)attribute.data(); } -Bitstream& Bitstream::operator>>(AttributePointer& attribute) { +Bitstream& Bitstream::operator>(AttributePointer& attribute) { QObject* object; *this >> object; attribute = AttributeRegistry::getInstance()->registerAttribute(static_cast(object)); return *this; } +Bitstream& Bitstream::operator<(const QScriptString& string) { + return *this << string.toString(); +} + +Bitstream& Bitstream::operator>(QScriptString& string) { + QString rawString; + *this >> rawString; + string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); + return *this; +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 0605cde50b..7798a00382 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -127,7 +128,7 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope int& offset = _transientOffsets[value]; if (offset == 0) { _idStreamer << (_lastPersistentID + (offset = ++_lastTransientOffset)); - _stream << value; + _stream < value; } else { _idStreamer << (_lastPersistentID + offset); @@ -148,7 +149,7 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope int offset = id - _lastPersistentID; typename QHash::iterator it = _transientValues.find(offset); if (it == _transientValues.end()) { - _stream >> value; + _stream > value; _transientValues.insert(offset, value); } else { @@ -167,6 +168,7 @@ public: QHash metaObjectOffsets; QHash typeStreamerOffsets; QHash attributeOffsets; + QHash scriptStringOffsets; }; class ReadMappings { @@ -174,6 +176,7 @@ public: QHash metaObjectValues; QHash typeStreamerValues; QHash attributeValues; + QHash scriptStringValues; }; /// Registers a metaobject under its name so that instances of it can be streamed. @@ -206,9 +209,6 @@ public: /// Resets to the initial state. void reset(); - /// Returns a reference to the attribute streamer. - RepeatedValueStreamer& getAttributeStreamer() { return _attributeStreamer; } - /// Returns the set of transient mappings gathered during writing and resets them. WriteMappings getAndResetWriteMappings(); @@ -239,6 +239,9 @@ public: Bitstream& operator<<(const QString& string); Bitstream& operator>>(QString& string); + Bitstream& operator<<(const QScriptString& string); + Bitstream& operator>>(QScriptString& string); + Bitstream& operator<<(const QUrl& url); Bitstream& operator>>(QUrl& url); @@ -266,6 +269,18 @@ public: Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); + Bitstream& operator<(const QMetaObject* metaObject); + Bitstream& operator>(const QMetaObject*& metaObject); + + Bitstream& operator<(const TypeStreamer* streamer); + Bitstream& operator>(const TypeStreamer*& streamer); + + Bitstream& operator<(const AttributePointer& attribute); + Bitstream& operator>(AttributePointer& attribute); + + Bitstream& operator<(const QScriptString& string); + Bitstream& operator>(QScriptString& string); + private: QDataStream& _underlying; @@ -275,6 +290,7 @@ private: RepeatedValueStreamer _metaObjectStreamer; RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; + RepeatedValueStreamer _scriptStringStreamer; static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 951378979b..b0e24405e6 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -136,7 +136,7 @@ void MetavoxelData::read(Bitstream& in) { in >> rootCount; for (int i = 0; i < rootCount; i++) { AttributePointer attribute; - in.getAttributeStreamer() >> attribute; + in >> attribute; MetavoxelNode*& root = _roots[attribute]; root = new MetavoxelNode(attribute); root->read(attribute, in); @@ -147,7 +147,7 @@ void MetavoxelData::write(Bitstream& out) const { out << _size; out << _roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - out.getAttributeStreamer() << it.key(); + out << it.key(); it.value()->write(it.key(), out); } } @@ -176,7 +176,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { in >> changedCount; for (int i = 0; i < changedCount; i++) { AttributePointer attribute; - in.getAttributeStreamer() >> attribute; + in >> attribute; MetavoxelNode*& root = _roots[attribute]; if (root) { MetavoxelNode* oldRoot = root; @@ -194,7 +194,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { in >> removedCount; for (int i = 0; i < removedCount; i++) { AttributePointer attribute; - in.getAttributeStreamer() >> attribute; + in >> attribute; _roots.take(attribute)->decrementReferenceCount(attribute); } } @@ -234,7 +234,7 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); if (it.value() != referenceRoot) { - out.getAttributeStreamer() << it.key(); + out << it.key(); if (referenceRoot) { it.value()->writeDelta(it.key(), *referenceRoot, out); } else { @@ -255,7 +255,7 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c for (QHash::const_iterator it = expandedReference->_roots.constBegin(); it != expandedReference->_roots.constEnd(); it++) { if (!_roots.contains(it.key())) { - out.getAttributeStreamer() << it.key(); + out << it.key(); } } diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 1620226386..58468bc271 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -21,9 +21,12 @@ #include #include "MetavoxelUtil.h" +#include "ScriptCache.h" +static int scriptHashType = qRegisterMetaType(); static int parameterizedURLType = qRegisterMetaType(); +REGISTER_SIMPLE_TYPE_STREAMER(ScriptHash) REGISTER_SIMPLE_TYPE_STREAMER(ParameterizedURL) class DelegatingItemEditorFactory : public QItemEditorFactory { @@ -106,7 +109,7 @@ bool Box::contains(const Box& other) const { other.minimum.z >= minimum.z && other.maximum.z <= maximum.z; } -ParameterizedURL::ParameterizedURL(const QUrl& url, const QVariantHash& parameters) : +ParameterizedURL::ParameterizedURL(const QUrl& url, const ScriptHash& parameters) : _url(url), _parameters(parameters) { } @@ -133,7 +136,7 @@ Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url) { Bitstream& operator>>(Bitstream& in, ParameterizedURL& url) { QUrl qurl; in >> qurl; - QVariantHash parameters; + ScriptHash parameters; in >> parameters; url = ParameterizedURL(qurl, parameters); return in; @@ -146,16 +149,47 @@ ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) : layout->setContentsMargins(QMargins()); setLayout(layout); - layout->addWidget(_line = new QLineEdit()); + QWidget* lineContainer = new QWidget(); + layout->addWidget(lineContainer); + + QHBoxLayout* lineLayout = new QHBoxLayout(); + lineContainer->setLayout(lineLayout); + lineLayout->setContentsMargins(QMargins()); + + lineLayout->addWidget(_line = new QLineEdit(), 1); connect(_line, SIGNAL(textChanged(const QString&)), SLOT(updateURL())); + + QPushButton* refresh = new QPushButton("..."); + connect(refresh, SIGNAL(clicked(bool)), SLOT(updateParameters())); + lineLayout->addWidget(refresh); } void ParameterizedURLEditor::setURL(const ParameterizedURL& url) { _url = url; _line->setText(url.getURL().toString()); + updateParameters(); } void ParameterizedURLEditor::updateURL() { _url = ParameterizedURL(_line->text()); emit urlChanged(_url); + if (_program) { + _program->disconnect(this); + } +} + +void ParameterizedURLEditor::updateParameters() { + if (_program) { + _program->disconnect(this); + } + _program = ScriptCache::getInstance()->getProgram(_url.getURL()); + if (_program->isLoaded()) { + continueUpdatingParameters(); + } else { + connect(_program.data(), SIGNAL(loaded()), SLOT(continueUpdatingParameters())); + } +} + +void ParameterizedURLEditor::continueUpdatingParameters() { + QScriptValue value = ScriptCache::getInstance()->getValue(_url.getURL())->getValue(); } diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 1ae77766f7..ad3084fe56 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -9,6 +9,7 @@ #ifndef __interface__MetavoxelUtil__ #define __interface__MetavoxelUtil__ +#include #include #include #include @@ -19,6 +20,7 @@ class QByteArray; class QLineEdit; class HifiSockAddr; +class NetworkProgram; /// Reads and returns the session ID from a datagram. /// \param[out] headerPlusIDSize the size of the header (including the session ID) within the data @@ -39,19 +41,23 @@ public: DECLARE_STREAMABLE_METATYPE(Box) +typedef QHash ScriptHash; + +Q_DECLARE_METATYPE(ScriptHash) + /// Combines a URL with a set of typed parameters. class ParameterizedURL { public: - ParameterizedURL(const QUrl& url = QUrl(), const QVariantHash& parameters = QVariantHash()); + ParameterizedURL(const QUrl& url = QUrl(), const ScriptHash& parameters = ScriptHash()); bool isValid() const { return _url.isValid(); } void setURL(const QUrl& url) { _url = url; } const QUrl& getURL() const { return _url; } - void setParameters(const QVariantHash& parameters) { _parameters = parameters; } - const QVariantHash& getParameters() const { return _parameters; } + void setParameters(const ScriptHash& parameters) { _parameters = parameters; } + const ScriptHash& getParameters() const { return _parameters; } bool operator==(const ParameterizedURL& other) const; bool operator!=(const ParameterizedURL& other) const; @@ -59,7 +65,7 @@ public: private: QUrl _url; - QVariantHash _parameters; + ScriptHash _parameters; }; uint qHash(const ParameterizedURL& url, uint seed = 0); @@ -89,10 +95,14 @@ public slots: private slots: void updateURL(); + void updateParameters(); + void continueUpdatingParameters(); private: ParameterizedURL _url; + QSharedPointer _program; + QLineEdit* _line; }; diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 18be6a1dea..8a6a5c39d9 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -15,7 +15,6 @@ #include #include "AttributeRegistry.h" -#include "MetavoxelUtil.h" #include "ScriptCache.h" ScriptCache* ScriptCache::getInstance() { @@ -49,7 +48,9 @@ QSharedPointer ScriptCache::getProgram(const QUrl& url) { QSharedPointer ScriptCache::getValue(const ParameterizedURL& url) { QSharedPointer value = _networkValues.value(url); if (value.isNull()) { - value = QSharedPointer(new NetworkValue(getProgram(url.getURL()), url.getParameters())); + value = QSharedPointer(url.getParameters().isEmpty() ? + (NetworkValue*)new RootNetworkValue(getProgram(url.getURL())) : + (NetworkValue*)new DerivedNetworkValue(getValue(url.getURL()), url.getParameters())); _networkValues.insert(url, value); } return value; @@ -94,6 +95,8 @@ void NetworkProgram::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo _reply->disconnect(this); _reply->deleteLater(); _reply = NULL; + + emit loaded(); } void NetworkProgram::handleReplyError() { @@ -112,14 +115,28 @@ void NetworkProgram::handleReplyError() { } } -NetworkValue::NetworkValue(const QSharedPointer& program, const QVariantHash& parameters) : - _program(program), - _parameters(parameters) { +NetworkValue::~NetworkValue() { } -QScriptValue& NetworkValue::getValue() { +RootNetworkValue::RootNetworkValue(const QSharedPointer& program) : + _program(program) { +} + +QScriptValue& RootNetworkValue::getValue() { if (!_value.isValid() && _program->isLoaded()) { _value = _program->getCache()->getEngine()->evaluate(_program->getProgram()); } return _value; } + +DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer& baseValue, const ScriptHash& parameters) : + _baseValue(baseValue), + _parameters(parameters) { +} + +QScriptValue& DerivedNetworkValue::getValue() { + if (!_value.isValid() && _baseValue->isLoaded()) { + _value = _baseValue->getValue(); + } + return _value; +} diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index 6d4561d669..a75ae18c44 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -15,17 +15,16 @@ #include #include #include -#include #include +#include "MetavoxelUtil.h" + class QNetworkAccessManager; class QNetworkReply; class QScriptEngine; -class QUrl; class NetworkProgram; class NetworkValue; -class ParameterizedURL; /// Maintains a cache of loaded scripts. class ScriptCache : public QObject { @@ -55,6 +54,7 @@ private: QScriptEngine* _engine; QHash > _networkPrograms; QHash > _networkValues; + }; /// A program loaded from the network. @@ -71,7 +71,11 @@ public: bool isLoaded() const { return !_program.isNull(); } const QScriptProgram& getProgram() const { return _program; } - + +signals: + + void loaded(); + private slots: void makeRequest(); @@ -87,21 +91,46 @@ private: QScriptProgram _program; }; -/// A value loaded from the network. +/// Abstract base class of values loaded from the network. class NetworkValue { public: - NetworkValue(const QSharedPointer& program, const QVariantHash& parameters); - + virtual ~NetworkValue(); + bool isLoaded() { return getValue().isValid(); } - QScriptValue& getValue(); + virtual QScriptValue& getValue() = 0; + +protected: + + QScriptValue _value; +}; + +/// The direct result of running a program. +class RootNetworkValue : public NetworkValue { +public: + + RootNetworkValue(const QSharedPointer& program); + + virtual QScriptValue& getValue(); private: QSharedPointer _program; - QVariantHash _parameters; - QScriptValue _value; +}; + +/// The result of running a program's generator using a set of arguments. +class DerivedNetworkValue : public NetworkValue { +public: + + DerivedNetworkValue(const QSharedPointer& baseValue, const ScriptHash& parameters); + + virtual QScriptValue& getValue(); + +private: + + QSharedPointer _baseValue; + ScriptHash _parameters; }; #endif /* defined(__interface__ScriptCache__) */ From be4f0814653dbe95c858d52a41dd569d7c480a2c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Feb 2014 19:55:11 -0800 Subject: [PATCH 15/24] More work on editing parameterized URLs. --- libraries/metavoxels/src/MetavoxelUtil.cpp | 28 ++++++++++++++++++- libraries/metavoxels/src/ScriptCache.cpp | 32 +++++++++++++++++++--- libraries/metavoxels/src/ScriptCache.h | 23 +++++++++++++++- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 58468bc271..0d3b5fc441 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -191,5 +192,30 @@ void ParameterizedURLEditor::updateParameters() { } void ParameterizedURLEditor::continueUpdatingParameters() { - QScriptValue value = ScriptCache::getInstance()->getValue(_url.getURL())->getValue(); + QVBoxLayout* layout = static_cast(this->layout()); + if (layout->count() > 1) { + QFormLayout* form = static_cast(layout->takeAt(1)); + for (int i = form->count() - 1; i >= 0; i--) { + QLayoutItem* item = form->takeAt(i); + if (item->widget()) { + delete item->widget(); + } + delete item; + } + delete form; + } + QSharedPointer value = ScriptCache::getInstance()->getValue(_url.getURL()); + const QList& parameters = static_cast(value.data())->getParameterInfo(); + if (parameters.isEmpty()) { + return; + } + QFormLayout* form = new QFormLayout(); + layout->addLayout(form); + foreach (const ParameterInfo& parameter, parameters) { + QWidget* editor = QItemEditorFactory::defaultFactory()->createEditor(parameter.type, NULL); + if (editor) { + form->addRow(parameter.name.toString() + ":", editor); + + } + } } diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 8a6a5c39d9..e99418c5c6 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -24,16 +24,21 @@ ScriptCache* ScriptCache::getInstance() { ScriptCache::ScriptCache() : _networkAccessManager(NULL), - _engine(new QScriptEngine(this)) { + _engine(NULL) { - AttributeRegistry::getInstance()->configureScriptEngine(_engine); + setEngine(new QScriptEngine(this)); } void ScriptCache::setEngine(QScriptEngine* engine) { - if (_engine->parent() == this) { + if (_engine && _engine->parent() == this) { delete _engine; } - _engine = engine; + AttributeRegistry::getInstance()->configureScriptEngine(_engine = engine); + _parametersString = engine->toStringHandle("parameters"); + _lengthString = engine->toStringHandle("length"); + _nameString = engine->toStringHandle("name"); + _typeString = engine->toStringHandle("type"); + _generatorString = engine->toStringHandle("generator"); } QSharedPointer ScriptCache::getProgram(const QUrl& url) { @@ -129,6 +134,24 @@ QScriptValue& RootNetworkValue::getValue() { return _value; } +const QList& RootNetworkValue::getParameterInfo() { + if (isLoaded() && _parameterInfo.isEmpty()) { + ScriptCache* cache = _program->getCache(); + QScriptEngine* engine = cache->getEngine(); + QScriptValue parameters = _value.property(cache->getParametersString()); + if (parameters.isArray()) { + int length = parameters.property(cache->getLengthString()).toInt32(); + for (int i = 0; i < length; i++) { + QScriptValue parameter = parameters.property(i); + ParameterInfo info = { engine->toStringHandle(parameter.property(cache->getNameString()).toString()), + QMetaType::type(parameter.property(cache->getTypeString()).toString().toUtf8().constData()) }; + _parameterInfo.append(info); + } + } + } + return _parameterInfo; +} + DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer& baseValue, const ScriptHash& parameters) : _baseValue(baseValue), _parameters(parameters) { @@ -137,6 +160,7 @@ DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer& bas QScriptValue& DerivedNetworkValue::getValue() { if (!_value.isValid() && _baseValue->isLoaded()) { _value = _baseValue->getValue(); + } return _value; } diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index a75ae18c44..1c51380c3f 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -10,6 +10,7 @@ #define __interface__ScriptCache__ #include +#include #include #include #include @@ -48,13 +49,23 @@ public: /// Loads a script value from the specified URL. QSharedPointer getValue(const ParameterizedURL& url); + const QScriptString& getParametersString() const { return _parametersString; } + const QScriptString& getLengthString() const { return _lengthString; } + const QScriptString& getNameString() const { return _nameString; } + const QScriptString& getTypeString() const { return _typeString; } + const QScriptString& getGeneratorString() const { return _generatorString; } + private: QNetworkAccessManager* _networkAccessManager; QScriptEngine* _engine; QHash > _networkPrograms; QHash > _networkValues; - + QScriptString _parametersString; + QScriptString _lengthString; + QScriptString _nameString; + QScriptString _typeString; + QScriptString _generatorString; }; /// A program loaded from the network. @@ -106,6 +117,13 @@ protected: QScriptValue _value; }; +/// Contains information about a script parameter. +class ParameterInfo { +public: + QScriptString name; + int type; +}; + /// The direct result of running a program. class RootNetworkValue : public NetworkValue { public: @@ -114,9 +132,12 @@ public: virtual QScriptValue& getValue(); + const QList& getParameterInfo(); + private: QSharedPointer _program; + QList _parameterInfo; }; /// The result of running a program's generator using a set of arguments. From 3a378dbeaf10590f7da1a6b0bc6defa11274f67c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Feb 2014 12:20:02 -0800 Subject: [PATCH 16/24] Working on extending the concept of a shared object. --- .../metavoxels/src/AttributeRegistry.cpp | 250 +----------------- libraries/metavoxels/src/AttributeRegistry.h | 95 +------ libraries/metavoxels/src/Bitstream.cpp | 55 +++- libraries/metavoxels/src/Bitstream.h | 25 +- .../metavoxels/src/DatagramSequencer.cpp | 75 +++++- libraries/metavoxels/src/DatagramSequencer.h | 53 ++++ libraries/metavoxels/src/SharedObject.cpp | 228 ++++++++++++++++ libraries/metavoxels/src/SharedObject.h | 115 ++++++++ 8 files changed, 538 insertions(+), 358 deletions(-) create mode 100644 libraries/metavoxels/src/SharedObject.cpp create mode 100644 libraries/metavoxels/src/SharedObject.h diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9c654514a1..fd5d742597 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -7,10 +7,6 @@ // #include -#include -#include -#include -#include #include #include #include @@ -192,111 +188,6 @@ void QRgbEditor::selectColor() { } } -SharedObject::SharedObject() : _referenceCount(0) { -} - -void SharedObject::incrementReferenceCount() { - _referenceCount++; -} - -void SharedObject::decrementReferenceCount() { - if (--_referenceCount == 0) { - delete this; - } -} - -SharedObject* SharedObject::clone() 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()); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored()) { - property.write(newObject, property.read(this)); - } - } - foreach (const QByteArray& propertyName, dynamicPropertyNames()) { - newObject->setProperty(propertyName, property(propertyName)); - } - return newObject; -} - -bool SharedObject::equals(const SharedObject* other) const { - // default behavior is to compare the properties - const QMetaObject* metaObject = this->metaObject(); - if (metaObject != other->metaObject()) { - return false; - } - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored() && property.read(this) != property.read(other)) { - return false; - } - } - QList dynamicPropertyNames = this->dynamicPropertyNames(); - if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) { - return false; - } - foreach (const QByteArray& propertyName, dynamicPropertyNames) { - if (property(propertyName) != other->property(propertyName)) { - return false; - } - } - return true; -} - -SharedObjectPointer::SharedObjectPointer(SharedObject* data) : _data(data) { - if (_data) { - _data->incrementReferenceCount(); - } -} - -SharedObjectPointer::SharedObjectPointer(const SharedObjectPointer& other) : _data(other._data) { - if (_data) { - _data->incrementReferenceCount(); - } -} - -SharedObjectPointer::~SharedObjectPointer() { - if (_data) { - _data->decrementReferenceCount(); - } -} - -void SharedObjectPointer::detach() { - if (_data && _data->getReferenceCount() > 1) { - _data->decrementReferenceCount(); - (_data = _data->clone())->incrementReferenceCount(); - } -} - -void SharedObjectPointer::reset() { - if (_data) { - _data->decrementReferenceCount(); - } - _data = NULL; -} - -SharedObjectPointer& SharedObjectPointer::operator=(SharedObject* data) { - if (_data) { - _data->decrementReferenceCount(); - } - if ((_data = data)) { - _data->incrementReferenceCount(); - } - return *this; -} - -SharedObjectPointer& SharedObjectPointer::operator=(const SharedObjectPointer& other) { - if (_data) { - _data->decrementReferenceCount(); - } - if ((_data = other._data)) { - _data->incrementReferenceCount(); - } - return *this; -} - SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), @@ -306,50 +197,25 @@ SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObj void SharedObjectAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { if (isLeaf) { - QObject* object; - in >> object; - *((SharedObjectPointer*)&value) = static_cast(object); + in >> *((SharedObjectPointer*)&value); } } void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) const { if (isLeaf) { - out << decodeInline(value).data(); + out << decodeInline(value); } } -bool SharedObjectAttribute::equal(void* first, void* second) const { - SharedObjectPointer firstPointer = decodeInline(first); - SharedObjectPointer secondPointer = decodeInline(second); - return (firstPointer == secondPointer) || (firstPointer && secondPointer && - firstPointer->equals(secondPointer.constData())); -} - bool SharedObjectAttribute::merge(void*& parent, void* children[]) const { - SharedObjectPointer& parentPointer = *(SharedObjectPointer*)&parent; - SharedObjectPointer childPointers[MERGE_COUNT]; - for (int i = 0; i < MERGE_COUNT; i++) { - childPointers[i] = decodeInline(children[i]); - } - if (childPointers[0]) { - for (int i = 1; i < MERGE_COUNT; i++) { - if (childPointers[0] == childPointers[i]) { - continue; - } - if (!(childPointers[i] && childPointers[0]->equals(childPointers[i].constData()))) { - parentPointer = _defaultValue; - return false; - } - } - } else { - for (int i = 1; i < MERGE_COUNT; i++) { - if (childPointers[i]) { - parentPointer = _defaultValue; - return false; - } + SharedObjectPointer firstChild = decodeInline(children[0]); + for (int i = 1; i < MERGE_COUNT; i++) { + if (firstChild != decodeInline(children[i])) { + *(SharedObjectPointer*)&parent = _defaultValue; + return false; } } - parentPointer = childPointers[0]; + *(SharedObjectPointer*)&parent = firstChild; return true; } @@ -362,103 +228,3 @@ QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { editor->setObject(_defaultValue); return editor; } - -SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) { - QVBoxLayout* layout = new QVBoxLayout(); - layout->setAlignment(Qt::AlignTop); - setLayout(layout); - - QFormLayout* form = new QFormLayout(); - layout->addLayout(form); - - form->addRow("Type:", _type = new QComboBox()); - _type->addItem("(none)"); - foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { - _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); - } - connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); -} - -void SharedObjectEditor::setObject(const SharedObjectPointer& object) { - _object = object; - const QMetaObject* metaObject = object ? object->metaObject() : NULL; - int index = _type->findData(QVariant::fromValue(metaObject)); - if (index != -1) { - // ensure that we call updateType to obtain the values - if (_type->currentIndex() == index) { - updateType(); - } else { - _type->setCurrentIndex(index); - } - } -} - -const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) { - while (propertyIndex < metaObject->propertyOffset()) { - metaObject = metaObject->superClass(); - } - return metaObject; -} - -void SharedObjectEditor::updateType() { - // delete the existing rows - if (layout()->count() > 1) { - QFormLayout* form = static_cast(layout()->takeAt(1)); - while (!form->isEmpty()) { - QLayoutItem* item = form->takeAt(0); - if (item->widget()) { - delete item->widget(); - } - delete item; - } - delete form; - } - const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); - if (metaObject == NULL) { - _object.reset(); - return; - } - QObject* oldObject = static_cast(_object.data()); - const QMetaObject* oldMetaObject = oldObject ? oldObject->metaObject() : NULL; - QObject* newObject = metaObject->newInstance(); - - QFormLayout* form = new QFormLayout(); - static_cast(layout())->addLayout(form); - for (int i = QObject::staticMetaObject.propertyCount(); i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (oldMetaObject && i < oldMetaObject->propertyCount() && - getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) { - // copy the state of the shared ancestry - property.write(newObject, property.read(oldObject)); - } - QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL); - if (widget) { - widget->setProperty("propertyIndex", i); - form->addRow(QByteArray(property.name()) + ':', widget); - QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); - const QMetaObject* widgetMetaObject = widget->metaObject(); - QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); - widgetProperty.write(widget, property.read(newObject)); - if (widgetProperty.hasNotifySignal()) { - connect(widget, QByteArray(SIGNAL()).append(widgetProperty.notifySignal().methodSignature()), - SLOT(propertyChanged())); - } - } - } - _object = static_cast(newObject); -} - -void SharedObjectEditor::propertyChanged() { - QFormLayout* form = static_cast(layout()->itemAt(1)); - for (int i = 0; i < form->rowCount(); i++) { - QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); - if (widget != sender()) { - continue; - } - _object.detach(); - QObject* object = _object.data(); - QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); - QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); - property.write(object, widget->property(valuePropertyName)); - } -} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 6442f82d0c..677b3b4dee 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -19,6 +19,7 @@ #include #include "Bitstream.h" +#include "SharedObject.h" class QComboBox; class QFormLayout; @@ -278,72 +279,6 @@ private: QColor _color; }; -/// A QObject with a reference count. -class SharedObject : public QObject { - Q_OBJECT - -public: - - SharedObject(); - - int getReferenceCount() const { return _referenceCount; } - void incrementReferenceCount(); - void decrementReferenceCount(); - - /// Creates a new clone of this object. - virtual SharedObject* clone() const; - - /// Tests this object for equality with another. - virtual bool equals(const SharedObject* other) const; - -private: - - int _referenceCount; -}; - -/// A pointer to a shared object. -class SharedObjectPointer { -public: - - SharedObjectPointer(SharedObject* data = NULL); - SharedObjectPointer(const SharedObjectPointer& other); - ~SharedObjectPointer(); - - SharedObject* data() { return _data; } - const SharedObject* data() const { return _data; } - const SharedObject* constData() const { return _data; } - - void detach(); - - void swap(SharedObjectPointer& other) { qSwap(_data, other._data); } - - void reset(); - - operator SharedObject*() { return _data; } - operator const SharedObject*() const { return _data; } - - bool operator!() const { return !_data; } - - bool operator!=(const SharedObjectPointer& other) const { return _data != other._data; } - - SharedObject& operator*() { return *_data; } - const SharedObject& operator*() const { return *_data; } - - SharedObject* operator->() { return _data; } - const SharedObject* operator->() const { return _data; } - - SharedObjectPointer& operator=(SharedObject* data); - SharedObjectPointer& operator=(const SharedObjectPointer& other); - - bool operator==(const SharedObjectPointer& other) const { return _data == other._data; } - -private: - - SharedObject* _data; -}; - -Q_DECLARE_METATYPE(SharedObjectPointer) - /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT @@ -357,41 +292,15 @@ public: virtual void read(Bitstream& in, void*& value, bool isLeaf) const; virtual void write(Bitstream& out, void* value, bool isLeaf) const; - virtual bool equal(void* first, void* second) const; - virtual bool merge(void*& parent, void* children[]) const; virtual void* createFromVariant(const QVariant& value) const; virtual QWidget* createEditor(QWidget* parent = NULL) const; - + private: const QMetaObject* _metaObject; }; -/// Allows editing shared object instances. -class SharedObjectEditor : public QWidget { - Q_OBJECT - Q_PROPERTY(SharedObjectPointer object MEMBER _object WRITE setObject USER true) - -public: - - SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent); - -public slots: - - void setObject(const SharedObjectPointer& object); - -private slots: - - void updateType(); - void propertyChanged(); - -private: - - QComboBox* _type; - SharedObjectPointer _object; -}; - #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index dfc2c9bd75..7a51d6f009 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -83,14 +83,16 @@ QList Bitstream::getMetaObjectSubClasses(const QMetaObject* return getMetaObjectSubClasses().values(metaObject); } -Bitstream::Bitstream(QDataStream& underlying) : +Bitstream::Bitstream(QDataStream& underlying, QObject* parent) : + QObject(parent), _underlying(underlying), _byte(0), _position(0), _metaObjectStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), - _scriptStringStreamer(*this) { + _scriptStringStreamer(*this), + _sharedObjectStreamer(*this) { } const int BITS_IN_BYTE = 8; @@ -148,7 +150,8 @@ Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), _typeStreamerStreamer.getAndResetTransientOffsets(), _attributeStreamer.getAndResetTransientOffsets(), - _scriptStringStreamer.getAndResetTransientOffsets() }; + _scriptStringStreamer.getAndResetTransientOffsets(), + _sharedObjectStreamer.getAndResetTransientOffsets() }; return mappings; } @@ -157,13 +160,15 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); + _sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets); } Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), _typeStreamerStreamer.getAndResetTransientValues(), _attributeStreamer.getAndResetTransientValues(), - _scriptStringStreamer.getAndResetTransientValues() }; + _scriptStringStreamer.getAndResetTransientValues(), + _sharedObjectStreamer.getAndResetTransientValues() }; return mappings; } @@ -172,6 +177,7 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); _attributeStreamer.persistTransientValues(mappings.attributeValues); _scriptStringStreamer.persistTransientValues(mappings.scriptStringValues); + _sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues); } Bitstream& Bitstream::operator<<(bool value) { @@ -244,16 +250,6 @@ Bitstream& Bitstream::operator>>(QString& string) { return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); } -Bitstream& Bitstream::operator<<(const QScriptString& string) { - _scriptStringStreamer << string; - return *this; -} - -Bitstream& Bitstream::operator>>(QScriptString& string) { - _scriptStringStreamer >> string; - return *this; -} - Bitstream& Bitstream::operator<<(const QUrl& url) { return *this << url.toString(); } @@ -379,6 +375,26 @@ Bitstream& Bitstream::operator>>(AttributePointer& attribute) { return *this; } +Bitstream& Bitstream::operator<<(const QScriptString& string) { + _scriptStringStreamer << string; + return *this; +} + +Bitstream& Bitstream::operator>>(QScriptString& string) { + _scriptStringStreamer >> string; + return *this; +} + +Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) { + _sharedObjectStreamer << object; + return *this; +} + +Bitstream& Bitstream::operator>>(SharedObjectPointer& object) { + _sharedObjectStreamer >> object; + return *this; +} + Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { return *this << (metaObject ? QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className())) : QByteArray()); @@ -435,6 +451,17 @@ Bitstream& Bitstream::operator>(QScriptString& string) { return *this; } +Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { + return *this << object.data(); +} + +Bitstream& Bitstream::operator>(SharedObjectPointer& object) { + QObject* rawObject; + *this >> rawObject; + object = static_cast(rawObject); + return *this; +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 7798a00382..b80cb8ef9b 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -18,10 +18,10 @@ #include +#include "SharedObject.h" + class QByteArray; class QDataStream; -struct QMetaObject; -class QObject; class QUrl; class Attribute; @@ -160,7 +160,9 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope } /// A stream for bit-aligned data. -class Bitstream { +class Bitstream : public QObject { + Q_OBJECT + public: class WriteMappings { @@ -169,6 +171,7 @@ public: QHash typeStreamerOffsets; QHash attributeOffsets; QHash scriptStringOffsets; + QHash sharedObjectOffsets; }; class ReadMappings { @@ -177,6 +180,7 @@ public: QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; + QHash sharedObjectValues; }; /// Registers a metaobject under its name so that instances of it can be streamed. @@ -191,7 +195,7 @@ public: static QList getMetaObjectSubClasses(const QMetaObject* metaObject); /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. - Bitstream(QDataStream& underlying); + Bitstream(QDataStream& underlying, QObject* parent = NULL); /// Writes a set of bits to the underlying stream. /// \param bits the number of bits to write @@ -239,9 +243,6 @@ public: Bitstream& operator<<(const QString& string); Bitstream& operator>>(QString& string); - Bitstream& operator<<(const QScriptString& string); - Bitstream& operator>>(QScriptString& string); - Bitstream& operator<<(const QUrl& url); Bitstream& operator>>(QUrl& url); @@ -269,6 +270,12 @@ public: Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); + Bitstream& operator<<(const QScriptString& string); + Bitstream& operator>>(QScriptString& string); + + Bitstream& operator<<(const SharedObjectPointer& object); + Bitstream& operator>>(SharedObjectPointer& object); + Bitstream& operator<(const QMetaObject* metaObject); Bitstream& operator>(const QMetaObject*& metaObject); @@ -281,6 +288,9 @@ public: Bitstream& operator<(const QScriptString& string); Bitstream& operator>(QScriptString& string); + Bitstream& operator<(const SharedObjectPointer& object); + Bitstream& operator>(SharedObjectPointer& object); + private: QDataStream& _underlying; @@ -291,6 +301,7 @@ private: RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; + RepeatedValueStreamer _sharedObjectStreamer; static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 83ef641b39..8ae40aefbe 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -14,6 +14,8 @@ const int MAX_DATAGRAM_SIZE = 1500; +const int DEFAULT_MAX_PACKET_SIZE = 3000; + DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), _outputStream(_outgoingPacketStream), @@ -26,7 +28,8 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _incomingPacketNumber(0), _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), _inputStream(_incomingPacketStream), - _receivedHighPriorityMessages(0) { + _receivedHighPriorityMessages(0), + _maxPacketSize(DEFAULT_MAX_PACKET_SIZE) { _outgoingPacketStream.setByteOrder(QDataStream::LittleEndian); _incomingDatagramStream.setByteOrder(QDataStream::LittleEndian); @@ -36,11 +39,31 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } +void DatagramSequencer::sendReliableMessage(const QVariant& data, int channel) { + +} + void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { HighPriorityMessage message = { data, _outgoingPacketNumber + 1 }; _highPriorityMessages.append(message); } +ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) { + ReliableChannel*& channel = _reliableOutputChannels[index]; + if (!channel) { + channel = new ReliableChannel(this); + } + return channel; +} + +ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) { + ReliableChannel*& channel = _reliableInputChannels[index]; + if (!channel) { + channel = new ReliableChannel(this); + } + return channel; +} + Bitstream& DatagramSequencer::startPacket() { // start with the list of acknowledgements _outgoingPacketStream << (quint32)_receiveRecords.size(); @@ -60,6 +83,16 @@ Bitstream& DatagramSequencer::startPacket() { void DatagramSequencer::endPacket() { _outputStream.flush(); + + // if we have space remaining, send some data from our reliable channels + int remaining = _maxPacketSize - _outgoingPacketStream.device()->pos(); + const int MINIMUM_RELIABLE_SIZE = sizeof(quint32) * 4; // count, channel number, offset, size + if (remaining > MINIMUM_RELIABLE_SIZE) { + appendReliableData(remaining); + } else { + _outgoingPacketStream << (quint32)0; + } + sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); _outgoingPacketStream.device()->seek(0); } @@ -150,8 +183,18 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } _receivedHighPriorityMessages = highPriorityMessageCount; - // alert external parties so that they can read the rest + // alert external parties so that they can read the middle emit readyToRead(_inputStream); + + // read the reliable data, if any + quint32 reliableChannels; + _incomingPacketStream >> reliableChannels; + for (int i = 0; i < reliableChannels; i++) { + quint32 channelIndex; + _incomingPacketStream >> channelIndex; + getReliableOutputChannel(channelIndex)->readData(_incomingPacketStream); + } + _incomingPacketStream.device()->seek(0); _inputStream.reset(); @@ -180,6 +223,15 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { } } +void DatagramSequencer::appendReliableData(int bytes) { + _outgoingPacketStream << (quint32)0; + + for (QHash::const_iterator it = _reliableOutputChannels.constBegin(); + it != _reliableOutputChannels.constEnd(); it++) { + + } +} + void DatagramSequencer::sendPacket(const QByteArray& packet) { QIODeviceOpener opener(&_outgoingDatagramBuffer, QIODevice::WriteOnly); @@ -213,3 +265,22 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { } while(offset < packet.size()); } +void ReliableChannel::sendMessage(const QVariant& message) { + _bitstream << message; +} + +ReliableChannel::ReliableChannel(DatagramSequencer* sequencer) : + QObject(sequencer), + _dataStream(&_buffer), + _bitstream(_dataStream), + _priority(1.0f) { + + _buffer.open(QIODevice::WriteOnly); + _dataStream.setByteOrder(QDataStream::LittleEndian); +} + +void ReliableChannel::readData(QDataStream& in) { + quint32 offset, size; + in >> offset >> size; + in.skipRawData(size); +} diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 4ef827abeb..38667b7f9f 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -17,6 +17,8 @@ #include "Bitstream.h" +class ReliableChannel; + /// Performs simple datagram sequencing, packet fragmentation and reassembly. class DatagramSequencer : public QObject { Q_OBJECT @@ -40,12 +42,27 @@ public: /// Returns the packet number of the sent packet at the specified index. int getSentPacketNumber(int index) const { return _sendRecords.at(index).packetNumber; } + /// Sends a normal-priority reliable message. + void sendReliableMessage(const QVariant& data, int channel = 0); + /// Adds a message to the high priority queue. Will be sent with every outgoing packet until received. void sendHighPriorityMessage(const QVariant& data); /// Returns a reference to the list of high priority messages not yet acknowledged. const QList& getHighPriorityMessages() const { return _highPriorityMessages; } + /// Sets the maximum packet size. This is a soft limit that determines how much + /// reliable data we include with each transmission. + void setMaxPacketSize(int maxPacketSize) { _maxPacketSize = maxPacketSize; } + + int getMaxPacketSize() const { return _maxPacketSize; } + + /// Returns the output channel at the specified index, creating it if necessary. + ReliableChannel* getReliableOutputChannel(int index = 0); + + /// Returns the intput channel at the + ReliableChannel* getReliableInputChannel(int index = 0); + /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet Bitstream& startPacket(); @@ -97,6 +114,9 @@ private: /// Notes that the described send was acknowledged by the other party. void sendRecordAcknowledged(const SendRecord& record); + /// Appends some reliable data to the outgoing packet. + void appendReliableData(int bytes); + /// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting /// readyToWrite) as necessary. void sendPacket(const QByteArray& packet); @@ -126,6 +146,39 @@ private: QList _highPriorityMessages; int _receivedHighPriorityMessages; + + int _maxPacketSize; + + QHash _reliableOutputChannels; + QHash _reliableInputChannels; +}; + +/// Represents a single reliable channel multiplexed onto the datagram sequence. +class ReliableChannel : public QObject { + Q_OBJECT + +public: + + QDataStream& getDataStream() { return _dataStream; } + Bitstream& getBitstream() { return _bitstream; } + + void setPriority(float priority) { _priority = priority; } + float getPriority() const { return _priority; } + + void sendMessage(const QVariant& message); + +private: + + friend class DatagramSequencer; + + ReliableChannel(DatagramSequencer* sequencer); + + void readData(QDataStream& in); + + QBuffer _buffer; + QDataStream _dataStream; + Bitstream _bitstream; + float _priority; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp new file mode 100644 index 0000000000..a3af307297 --- /dev/null +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -0,0 +1,228 @@ +// +// SharedObject.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 2/5/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include +#include +#include + +#include "Bitstream.h" +#include "SharedObject.h" + +SharedObject::SharedObject() : _referenceCount(0) { +} + +void SharedObject::incrementReferenceCount() { + _referenceCount++; +} + +void SharedObject::decrementReferenceCount() { + if (--_referenceCount == 0) { + delete this; + + } else if (_referenceCount == 1) { + emit referenceCountDroppedToOne(); + } +} + +SharedObject* SharedObject::clone() 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()); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored()) { + property.write(newObject, property.read(this)); + } + } + foreach (const QByteArray& propertyName, dynamicPropertyNames()) { + newObject->setProperty(propertyName, property(propertyName)); + } + return newObject; +} + +bool SharedObject::equals(const SharedObject* other) const { + // default behavior is to compare the properties + const QMetaObject* metaObject = this->metaObject(); + if (metaObject != other->metaObject()) { + return false; + } + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored() && property.read(this) != property.read(other)) { + return false; + } + } + QList dynamicPropertyNames = this->dynamicPropertyNames(); + if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) { + return false; + } + foreach (const QByteArray& propertyName, dynamicPropertyNames) { + if (property(propertyName) != other->property(propertyName)) { + return false; + } + } + return true; +} + +SharedObjectPointer::SharedObjectPointer(SharedObject* data) : _data(data) { + if (_data) { + _data->incrementReferenceCount(); + } +} + +SharedObjectPointer::SharedObjectPointer(const SharedObjectPointer& other) : _data(other._data) { + if (_data) { + _data->incrementReferenceCount(); + } +} + +SharedObjectPointer::~SharedObjectPointer() { + if (_data) { + _data->decrementReferenceCount(); + } +} + +void SharedObjectPointer::detach() { + if (_data && _data->getReferenceCount() > 1) { + _data->decrementReferenceCount(); + (_data = _data->clone())->incrementReferenceCount(); + } +} + +void SharedObjectPointer::reset() { + if (_data) { + _data->decrementReferenceCount(); + } + _data = NULL; +} + +SharedObjectPointer& SharedObjectPointer::operator=(SharedObject* data) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +SharedObjectPointer& SharedObjectPointer::operator=(const SharedObjectPointer& other) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = other._data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +uint qHash(const SharedObjectPointer& pointer, uint seed) { + return qHash(pointer.data(), seed); +} + +SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + QFormLayout* form = new QFormLayout(); + layout->addLayout(form); + + form->addRow("Type:", _type = new QComboBox()); + _type->addItem("(none)"); + foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { + _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); + } + connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); +} + +void SharedObjectEditor::setObject(const SharedObjectPointer& object) { + _object = object; + const QMetaObject* metaObject = object ? object->metaObject() : NULL; + int index = _type->findData(QVariant::fromValue(metaObject)); + if (index != -1) { + // ensure that we call updateType to obtain the values + if (_type->currentIndex() == index) { + updateType(); + } else { + _type->setCurrentIndex(index); + } + } +} + +const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) { + while (propertyIndex < metaObject->propertyOffset()) { + metaObject = metaObject->superClass(); + } + return metaObject; +} + +void SharedObjectEditor::updateType() { + // delete the existing rows + if (layout()->count() > 1) { + QFormLayout* form = static_cast(layout()->takeAt(1)); + while (!form->isEmpty()) { + QLayoutItem* item = form->takeAt(0); + if (item->widget()) { + delete item->widget(); + } + delete item; + } + delete form; + } + const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); + if (metaObject == NULL) { + _object.reset(); + return; + } + QObject* oldObject = static_cast(_object.data()); + const QMetaObject* oldMetaObject = oldObject ? oldObject->metaObject() : NULL; + QObject* newObject = metaObject->newInstance(); + + QFormLayout* form = new QFormLayout(); + static_cast(layout())->addLayout(form); + for (int i = QObject::staticMetaObject.propertyCount(); i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (oldMetaObject && i < oldMetaObject->propertyCount() && + getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) { + // copy the state of the shared ancestry + property.write(newObject, property.read(oldObject)); + } + QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL); + if (widget) { + widget->setProperty("propertyIndex", i); + form->addRow(QByteArray(property.name()) + ':', widget); + QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); + const QMetaObject* widgetMetaObject = widget->metaObject(); + QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); + widgetProperty.write(widget, property.read(newObject)); + if (widgetProperty.hasNotifySignal()) { + connect(widget, QByteArray(SIGNAL()).append(widgetProperty.notifySignal().methodSignature()), + SLOT(propertyChanged())); + } + } + } + _object = static_cast(newObject); +} + +void SharedObjectEditor::propertyChanged() { + QFormLayout* form = static_cast(layout()->itemAt(1)); + for (int i = 0; i < form->rowCount(); i++) { + QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); + if (widget != sender()) { + continue; + } + _object.detach(); + QObject* object = _object.data(); + QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); + QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); + property.write(object, widget->property(valuePropertyName)); + } +} diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h new file mode 100644 index 0000000000..e439c4c7f0 --- /dev/null +++ b/libraries/metavoxels/src/SharedObject.h @@ -0,0 +1,115 @@ +// +// SharedObject.h +// metavoxels +// +// Created by Andrzej Kapolka on 2/5/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__SharedObject__ +#define __interface__SharedObject__ + +#include +#include +#include + +class QComboBox; + +/// A QObject that may be shared over the network. +class SharedObject : public QObject { + Q_OBJECT + +public: + + SharedObject(); + + int getReferenceCount() const { return _referenceCount; } + void incrementReferenceCount(); + void decrementReferenceCount(); + + /// Creates a new clone of this object. + virtual SharedObject* clone() const; + + /// Tests this object for equality with another. + virtual bool equals(const SharedObject* other) const; + +signals: + + /// Emitted when the reference count drops to one. + void referenceCountDroppedToOne(); + +private: + + int _referenceCount; +}; + +/// A pointer to a shared object. +class SharedObjectPointer { +public: + + SharedObjectPointer(SharedObject* data = NULL); + SharedObjectPointer(const SharedObjectPointer& other); + ~SharedObjectPointer(); + + SharedObject* data() { return _data; } + const SharedObject* data() const { return _data; } + const SharedObject* constData() const { return _data; } + + void detach(); + + void swap(SharedObjectPointer& other) { qSwap(_data, other._data); } + + void reset(); + + operator SharedObject*() { return _data; } + operator const SharedObject*() const { return _data; } + + bool operator!() const { return !_data; } + + bool operator!=(const SharedObjectPointer& other) const { return _data != other._data; } + + SharedObject& operator*() { return *_data; } + const SharedObject& operator*() const { return *_data; } + + SharedObject* operator->() { return _data; } + const SharedObject* operator->() const { return _data; } + + SharedObjectPointer& operator=(SharedObject* data); + SharedObjectPointer& operator=(const SharedObjectPointer& other); + + bool operator==(const SharedObjectPointer& other) const { return _data == other._data; } + +private: + + SharedObject* _data; +}; + +Q_DECLARE_METATYPE(SharedObjectPointer) + +uint qHash(const SharedObjectPointer& pointer, uint seed = 0); + +/// Allows editing shared object instances. +class SharedObjectEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(SharedObjectPointer object MEMBER _object WRITE setObject USER true) + +public: + + SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent); + +public slots: + + void setObject(const SharedObjectPointer& object); + +private slots: + + void updateType(); + void propertyChanged(); + +private: + + QComboBox* _type; + SharedObjectPointer _object; +}; + +#endif /* defined(__interface__SharedObject__) */ From 5e9916302bb281e7046653727c2ee1751f050e77 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Feb 2014 13:28:19 -0800 Subject: [PATCH 17/24] Working on clearing shared object references. --- libraries/metavoxels/src/Bitstream.cpp | 14 ++++++++++++++ libraries/metavoxels/src/Bitstream.h | 10 ++++++++++ libraries/metavoxels/src/DatagramSequencer.cpp | 13 +++++++++++++ libraries/metavoxels/src/DatagramSequencer.h | 10 +++++++++- libraries/metavoxels/src/MetavoxelMessages.h | 11 +++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 7a51d6f009..390d9f8f30 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -161,6 +161,14 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); _sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets); + + // find out when shared objects' reference counts drop to one 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(referenceCountDroppedToOne()), SLOT(clearSharedObject())); + } + } } Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { @@ -462,6 +470,12 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { return *this; } +void Bitstream::clearSharedObject() { + SharedObjectPointer object(static_cast(sender())); + object->disconnect(this); + emit sharedObjectCleared(_sharedObjectStreamer.takePersistentID(object)); +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index b80cb8ef9b..6122c5c3be 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -67,6 +67,8 @@ public: void persistTransientValues(const QHash& transientValues); + int takePersistentID(T value) { return _persistentIDs.take(value); } + RepeatedValueStreamer& operator<<(T value); RepeatedValueStreamer& operator>>(T& value); @@ -290,7 +292,15 @@ public: Bitstream& operator<(const SharedObjectPointer& object); Bitstream& operator>(SharedObjectPointer& object); + +signals: + + void sharedObjectCleared(int id); + +private slots: + void clearSharedObject(); + private: QDataStream& _underlying; diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 8ae40aefbe..b51f573712 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -36,6 +36,8 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _incomingPacketStream.setByteOrder(QDataStream::LittleEndian); _outgoingDatagramStream.setByteOrder(QDataStream::LittleEndian); + connect(&_outputStream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); + memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } @@ -203,6 +205,10 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _receiveRecords.append(record); } +void DatagramSequencer::sendClearSharedObjectMessage(int id) { + qDebug() << "cleared " << id; +} + void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { // stop acknowledging the recorded packets while (!_receiveRecords.isEmpty() && _receiveRecords.first().packetNumber <= record.lastReceivedPacketNumber) { @@ -269,6 +275,9 @@ void ReliableChannel::sendMessage(const QVariant& message) { _bitstream << message; } +void ReliableChannel::sendClearSharedObjectMessage(int id) { +} + ReliableChannel::ReliableChannel(DatagramSequencer* sequencer) : QObject(sequencer), _dataStream(&_buffer), @@ -277,10 +286,14 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer) : _buffer.open(QIODevice::WriteOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); + + connect(&_bitstream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); } void ReliableChannel::readData(QDataStream& in) { quint32 offset, size; in >> offset >> size; + + in.skipRawData(size); } diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 38667b7f9f..e3c884938c 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -92,7 +92,11 @@ signals: /// Emitted when our acknowledgement of a received packet has been acknowledged by the remote side. /// \param index the index of the packet in our list of receive records void receiveAcknowledged(int index); - + +private slots: + + void sendClearSharedObjectMessage(int id); + private: class SendRecord { @@ -167,6 +171,10 @@ public: void sendMessage(const QVariant& message); +private slots: + + void sendClearSharedObjectMessage(int id); + private: friend class DatagramSequencer; diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 25f4ffe422..c3cc78c5bc 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -21,6 +21,17 @@ class CloseSessionMessage { DECLARE_STREAMABLE_METATYPE(CloseSessionMessage) +/// Clears the mapping for a shared object. +class ClearSharedObjectMessage { + STREAMABLE + +public: + + STREAM int id; +}; + +DECLARE_STREAMABLE_METATYPE(ClearSharedObjectMessage) + /// A message containing the state of a client. class ClientStateMessage { STREAMABLE From e77feb8efd43db09af2e48826a0a4f7d03b15ce0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Feb 2014 16:19:16 -0800 Subject: [PATCH 18/24] More work on reliable transfer. --- libraries/metavoxels/src/Bitstream.h | 5 + .../metavoxels/src/DatagramSequencer.cpp | 151 ++++++++++++++---- libraries/metavoxels/src/DatagramSequencer.h | 44 ++++- 3 files changed, 166 insertions(+), 34 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 6122c5c3be..fe8b296f61 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -69,6 +69,8 @@ public: int takePersistentID(T value) { return _persistentIDs.take(value); } + void removePersistentValue(int id) { _persistentValues.remove(id); } + RepeatedValueStreamer& operator<<(T value); RepeatedValueStreamer& operator>>(T& value); @@ -227,6 +229,9 @@ public: /// Persists a set of read mappings recorded earlier. void persistReadMappings(const ReadMappings& mappings); + /// Removes a shared object from the read mappings. + void clearSharedObject(int id) { _sharedObjectStreamer.removePersistentValue(id); } + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index b51f573712..e3e3d65838 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -11,6 +11,7 @@ #include #include "DatagramSequencer.h" +#include "MetavoxelMessages.h" const int MAX_DATAGRAM_SIZE = 1500; @@ -41,10 +42,6 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } -void DatagramSequencer::sendReliableMessage(const QVariant& data, int channel) { - -} - void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { HighPriorityMessage message = { data, _outgoingPacketNumber + 1 }; _highPriorityMessages.append(message); @@ -53,7 +50,7 @@ void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) { ReliableChannel*& channel = _reliableOutputChannels[index]; if (!channel) { - channel = new ReliableChannel(this); + channel = new ReliableChannel(this, index); } return channel; } @@ -61,7 +58,7 @@ ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) { ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) { ReliableChannel*& channel = _reliableInputChannels[index]; if (!channel) { - channel = new ReliableChannel(this); + channel = new ReliableChannel(this, index); } return channel; } @@ -88,14 +85,15 @@ void DatagramSequencer::endPacket() { // if we have space remaining, send some data from our reliable channels int remaining = _maxPacketSize - _outgoingPacketStream.device()->pos(); - const int MINIMUM_RELIABLE_SIZE = sizeof(quint32) * 4; // count, channel number, offset, size + const int MINIMUM_RELIABLE_SIZE = sizeof(quint32) * 5; // count, channel number, segment count, offset, size + QVector spans; if (remaining > MINIMUM_RELIABLE_SIZE) { - appendReliableData(remaining); + appendReliableData(remaining, spans); } else { _outgoingPacketStream << (quint32)0; } - sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); + sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos()), spans); _outgoingPacketStream.device()->seek(0); } @@ -180,7 +178,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { QVariant data; _inputStream >> data; if (i >= _receivedHighPriorityMessages) { - emit receivedHighPriorityMessage(data); + handleHighPriorityMessage(data); } } _receivedHighPriorityMessages = highPriorityMessageCount; @@ -206,7 +204,9 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } void DatagramSequencer::sendClearSharedObjectMessage(int id) { - qDebug() << "cleared " << id; + // for now, high priority + ClearSharedObjectMessage message = { id }; + sendHighPriorityMessage(QVariant::fromValue(message)); } void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { @@ -227,18 +227,46 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { break; } } -} - -void DatagramSequencer::appendReliableData(int bytes) { - _outgoingPacketStream << (quint32)0; - - for (QHash::const_iterator it = _reliableOutputChannels.constBegin(); - it != _reliableOutputChannels.constEnd(); it++) { - + + // acknowledge the received spans + foreach (const ChannelSpan& span, record.spans) { + getReliableOutputChannel(span.channel)->spanAcknowledged(span); } } -void DatagramSequencer::sendPacket(const QByteArray& packet) { +void DatagramSequencer::appendReliableData(int bytes, QVector& spans) { + // gather total number of bytes to write, priority + int totalBytes = 0; + float totalPriority = 0.0f; + int totalChannels = 0; + foreach (ReliableChannel* channel, _reliableOutputChannels) { + int channelBytes = channel->getBytesAvailable(); + if (channelBytes > 0) { + totalBytes += channelBytes; + totalPriority += channel->getPriority(); + totalChannels++; + } + } + _outgoingPacketStream << (quint32)totalChannels; + if (totalChannels == 0) { + return; + } + totalBytes = qMin(bytes, totalBytes); + + foreach (ReliableChannel* channel, _reliableOutputChannels) { + int channelBytes = channel->getBytesAvailable(); + if (channelBytes == 0) { + continue; + } + _outgoingPacketStream << (quint32)channel->getIndex(); + channelBytes = qMin(channelBytes, (int)(totalBytes * channel->getPriority() / totalPriority)); + channel->writeData(_outgoingPacketStream, channelBytes, spans); + totalBytes -= channelBytes; + totalPriority -= channel->getPriority(); + } +} + +void DatagramSequencer::sendPacket(const QByteArray& packet, const QVector& spans) { QIODeviceOpener opener(&_outgoingDatagramBuffer, QIODevice::WriteOnly); // increment the packet number @@ -246,7 +274,7 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { // record the send SendRecord record = { _outgoingPacketNumber, _receiveRecords.isEmpty() ? 0 : _receiveRecords.last().packetNumber, - _outputStream.getAndResetWriteMappings() }; + _outputStream.getAndResetWriteMappings(), spans }; _sendRecords.append(record); // write the sequence number and size, which are the same between all fragments @@ -271,18 +299,36 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { } while(offset < packet.size()); } +void DatagramSequencer::handleHighPriorityMessage(const QVariant& data) { + if (data.userType() == ClearSharedObjectMessage::Type) { + _inputStream.clearSharedObject(data.value().id); + + } else { + emit receivedHighPriorityMessage(data); + } +} + +int ReliableChannel::getBytesAvailable() const { + return _buffer.pos() - _sent; +} + void ReliableChannel::sendMessage(const QVariant& message) { _bitstream << message; } void ReliableChannel::sendClearSharedObjectMessage(int id) { + ClearSharedObjectMessage message = { id }; + sendMessage(QVariant::fromValue(message)); } -ReliableChannel::ReliableChannel(DatagramSequencer* sequencer) : +ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index) : QObject(sequencer), + _index(index), _dataStream(&_buffer), _bitstream(_dataStream), - _priority(1.0f) { + _priority(1.0f), + _offset(0), + _sent(0) { _buffer.open(QIODevice::WriteOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); @@ -290,10 +336,61 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer) : connect(&_bitstream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); } -void ReliableChannel::readData(QDataStream& in) { - quint32 offset, size; - in >> offset >> size; +void ReliableChannel::writeData(QDataStream& out, int bytes, QVector& spans) { + // determine how many spans we can send + int remainingBytes = bytes; + int position = 0; + int spanCount = 0; + foreach (const RemainingSpan& remainingSpan, _remainingSpans) { + if (remainingBytes == 0) { + break; + } + int spanBytes = qMin(remainingSpan.unacknowledged, remainingBytes); + remainingBytes -= spanBytes; + spanCount++; + position += remainingSpan.unacknowledged + remainingSpan.acknowledged; + } + if (remainingBytes > 0 && position < _buffer.pos()) { + spanCount++; + } + out << (quint32)spanCount; - in.skipRawData(size); + remainingBytes = bytes; + position = 0; + foreach (const RemainingSpan& remainingSpan, _remainingSpans) { + if (remainingBytes == 0) { + break; + } + int spanBytes = qMin(remainingSpan.unacknowledged, remainingBytes); + writeSpan(out, position, spanBytes, spans); + remainingBytes -= spanBytes; + position += remainingSpan.unacknowledged + remainingSpan.acknowledged; + } + if (remainingBytes > 0 && position < _buffer.pos()) { + int spanBytes = qMin((int)_buffer.pos() - position, remainingBytes); + writeSpan(out, position, spanBytes, spans); + } } + +void ReliableChannel::writeSpan(QDataStream& out, int position, int length, QVector& spans) { + DatagramSequencer::ChannelSpan span = { _index, _offset + position, length }; + spans.append(span); + out << (quint32)span.offset; + out << (quint32)length; + out.writeRawData(_buffer.data().constData() + position, length); +} + +void ReliableChannel::spanAcknowledged(const DatagramSequencer::ChannelSpan& span) { +} + +void ReliableChannel::readData(QDataStream& in) { + quint32 segments; + in >> segments; + for (int i = 0; i < segments; i++) { + quint32 offset, size; + in >> offset >> size; + in.skipRawData(size); + } +} + diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index e3c884938c..b53ace2b82 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "Bitstream.h" @@ -42,9 +43,6 @@ public: /// Returns the packet number of the sent packet at the specified index. int getSentPacketNumber(int index) const { return _sendRecords.at(index).packetNumber; } - /// Sends a normal-priority reliable message. - void sendReliableMessage(const QVariant& data, int channel = 0); - /// Adds a message to the high priority queue. Will be sent with every outgoing packet until received. void sendHighPriorityMessage(const QVariant& data); @@ -96,14 +94,24 @@ signals: private slots: void sendClearSharedObjectMessage(int id); - + private: + friend class ReliableChannel; + + class ChannelSpan { + public: + int channel; + int offset; + int length; + }; + class SendRecord { public: int packetNumber; int lastReceivedPacketNumber; Bitstream::WriteMappings mappings; + QVector spans; }; class ReceiveRecord { @@ -119,11 +127,13 @@ private: void sendRecordAcknowledged(const SendRecord& record); /// Appends some reliable data to the outgoing packet. - void appendReliableData(int bytes); + void appendReliableData(int bytes, QVector& spans); /// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting /// readyToWrite) as necessary. - void sendPacket(const QByteArray& packet); + void sendPacket(const QByteArray& packet, const QVector& spans); + + void handleHighPriorityMessage(const QVariant& data); QList _sendRecords; QList _receiveRecords; @@ -163,12 +173,16 @@ class ReliableChannel : public QObject { public: + int getIndex() const { return _index; } + QDataStream& getDataStream() { return _dataStream; } Bitstream& getBitstream() { return _bitstream; } void setPriority(float priority) { _priority = priority; } float getPriority() const { return _priority; } + int getBytesAvailable() const; + void sendMessage(const QVariant& message); private slots: @@ -179,14 +193,30 @@ private: friend class DatagramSequencer; - ReliableChannel(DatagramSequencer* sequencer); + class RemainingSpan { + public: + int unacknowledged; + int acknowledged; + }; + + ReliableChannel(DatagramSequencer* sequencer, int index); + + void writeData(QDataStream& out, int bytes, QVector& spans); + void writeSpan(QDataStream& out, int position, int length, QVector& spans); + + void spanAcknowledged(const DatagramSequencer::ChannelSpan& span); void readData(QDataStream& in); + int _index; QBuffer _buffer; QDataStream _dataStream; Bitstream _bitstream; float _priority; + + int _offset; + int _sent; + QList _remainingSpans; }; #endif /* defined(__interface__DatagramSequencer__) */ From 98a432d3e5673bdb05dfbed8ba991eed0428afac Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Feb 2014 19:24:16 -0800 Subject: [PATCH 19/24] Punting on the span tracking thing for now. --- .../metavoxels/src/DatagramSequencer.cpp | 42 +++---------------- libraries/metavoxels/src/DatagramSequencer.h | 9 +--- 2 files changed, 7 insertions(+), 44 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index e3e3d65838..fc5d84e322 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -309,7 +309,7 @@ void DatagramSequencer::handleHighPriorityMessage(const QVariant& data) { } int ReliableChannel::getBytesAvailable() const { - return _buffer.pos() - _sent; + return _buffer.size() - _acknowledged; } void ReliableChannel::sendMessage(const QVariant& message) { @@ -328,7 +328,7 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index) : _bitstream(_dataStream), _priority(1.0f), _offset(0), - _sent(0) { + _acknowledged(0) { _buffer.open(QIODevice::WriteOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); @@ -337,40 +337,8 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index) : } void ReliableChannel::writeData(QDataStream& out, int bytes, QVector& spans) { - // determine how many spans we can send - int remainingBytes = bytes; - int position = 0; - int spanCount = 0; - - foreach (const RemainingSpan& remainingSpan, _remainingSpans) { - if (remainingBytes == 0) { - break; - } - int spanBytes = qMin(remainingSpan.unacknowledged, remainingBytes); - remainingBytes -= spanBytes; - spanCount++; - position += remainingSpan.unacknowledged + remainingSpan.acknowledged; - } - if (remainingBytes > 0 && position < _buffer.pos()) { - spanCount++; - } - out << (quint32)spanCount; - - remainingBytes = bytes; - position = 0; - foreach (const RemainingSpan& remainingSpan, _remainingSpans) { - if (remainingBytes == 0) { - break; - } - int spanBytes = qMin(remainingSpan.unacknowledged, remainingBytes); - writeSpan(out, position, spanBytes, spans); - remainingBytes -= spanBytes; - position += remainingSpan.unacknowledged + remainingSpan.acknowledged; - } - if (remainingBytes > 0 && position < _buffer.pos()) { - int spanBytes = qMin((int)_buffer.pos() - position, remainingBytes); - writeSpan(out, position, spanBytes, spans); - } + // nothing for now + out << (quint32)0; } void ReliableChannel::writeSpan(QDataStream& out, int position, int length, QVector& spans) { @@ -382,12 +350,14 @@ void ReliableChannel::writeSpan(QDataStream& out, int position, int length, QVec } void ReliableChannel::spanAcknowledged(const DatagramSequencer::ChannelSpan& span) { + // no-op for now } void ReliableChannel::readData(QDataStream& in) { quint32 segments; in >> segments; for (int i = 0; i < segments; i++) { + // ignore for now quint32 offset, size; in >> offset >> size; in.skipRawData(size); diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index b53ace2b82..08fd19f57e 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -193,12 +193,6 @@ private: friend class DatagramSequencer; - class RemainingSpan { - public: - int unacknowledged; - int acknowledged; - }; - ReliableChannel(DatagramSequencer* sequencer, int index); void writeData(QDataStream& out, int bytes, QVector& spans); @@ -215,8 +209,7 @@ private: float _priority; int _offset; - int _sent; - QList _remainingSpans; + int _acknowledged; }; #endif /* defined(__interface__DatagramSequencer__) */ From 6b4c8b62c63cb5ae404e8c0fb8683f0e8d9d4727 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Feb 2014 21:28:36 -0800 Subject: [PATCH 20/24] Back down the rabbit hole I go, weee! --- .../metavoxels/src/DatagramSequencer.cpp | 102 ++++++++++++++++-- libraries/metavoxels/src/DatagramSequencer.h | 31 +++++- 2 files changed, 121 insertions(+), 12 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index fc5d84e322..d372383f0d 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -50,7 +50,7 @@ void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) { ReliableChannel*& channel = _reliableOutputChannels[index]; if (!channel) { - channel = new ReliableChannel(this, index); + channel = new ReliableChannel(this, index, true); } return channel; } @@ -58,7 +58,7 @@ ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) { ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) { ReliableChannel*& channel = _reliableInputChannels[index]; if (!channel) { - channel = new ReliableChannel(this, index); + channel = new ReliableChannel(this, index, false); } return channel; } @@ -308,8 +308,58 @@ void DatagramSequencer::handleHighPriorityMessage(const QVariant& data) { } } +SpanList::SpanList() : _totalSet(0) { +} + +int SpanList::set(int offset, int length) { + // see if it intersects the front of the list + if (offset <= 0) { + int intersection = offset + length; + return (intersection > 0) ? setSpans(_spans.begin(), intersection) : 0; + } + + // look for an intersection within the list + int position = 0; + for (QList::iterator it = _spans.begin(); it != _spans.end(); it++) { + position += it->unset; + if (offset <= position) { + + return 0; + } + + position += it->set; + if (offset <= position) { + + return 0; + } + } + + // add to end of list + Span span = { offset - position, length }; + _spans.append(span); + + return 0; +} + +int SpanList::setSpans(QList::iterator it, int length) { + int remainingLength = length; + int totalRemoved = 0; + for (; it != _spans.end(); it++) { + if (remainingLength < it->unset) { + it->unset -= remainingLength; + totalRemoved += remainingLength; + break; + } + int combined = it->unset + it->set; + remainingLength = qMax(remainingLength - combined, 0); + totalRemoved += combined; + it = _spans.erase(it); + } + return qMax(length, totalRemoved); +} + int ReliableChannel::getBytesAvailable() const { - return _buffer.size() - _acknowledged; + return _buffer.size() - _acknowledged.getTotalSet(); } void ReliableChannel::sendMessage(const QVariant& message) { @@ -321,16 +371,15 @@ void ReliableChannel::sendClearSharedObjectMessage(int id) { sendMessage(QVariant::fromValue(message)); } -ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index) : +ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool output) : QObject(sequencer), _index(index), _dataStream(&_buffer), _bitstream(_dataStream), _priority(1.0f), - _offset(0), - _acknowledged(0) { + _offset(0) { - _buffer.open(QIODevice::WriteOnly); + _buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); connect(&_bitstream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); @@ -350,17 +399,50 @@ void ReliableChannel::writeSpan(QDataStream& out, int position, int length, QVec } void ReliableChannel::spanAcknowledged(const DatagramSequencer::ChannelSpan& span) { - // no-op for now + int acknowledged = _acknowledged.set(span.offset - _offset, span.length); + if (acknowledged > 0) { + // TODO: better way of pruning buffer + _buffer.buffer() = _buffer.buffer().right(_buffer.size() - acknowledged); + _buffer.seek(_buffer.size()); + _offset += acknowledged; + } } void ReliableChannel::readData(QDataStream& in) { quint32 segments; in >> segments; for (int i = 0; i < segments; i++) { - // ignore for now quint32 offset, size; in >> offset >> size; - in.skipRawData(size); + + int position = offset - _offset; + int end = position + size; + if (_assemblyBuffer.size() < end) { + _assemblyBuffer.resize(end); + } + if (end <= 0) { + in.skipRawData(size); + } else if (position < 0) { + in.skipRawData(-position); + in.readRawData(_assemblyBuffer.data(), size + position); + } else { + in.readRawData(_assemblyBuffer.data() + position, size); + } + int acknowledged = _acknowledged.set(position, size); + if (acknowledged > 0) { + // TODO: better way of pruning buffer + _buffer.buffer().append(_assemblyBuffer.constData(), acknowledged); + emit _buffer.readyRead(); + _assemblyBuffer = _assemblyBuffer.right(_assemblyBuffer.size() - acknowledged); + _offset += acknowledged; + } + } + + // TODO: better way of pruning buffer? + const int PRUNE_SIZE = 8192; + if (_buffer.pos() > PRUNE_SIZE) { + _buffer.buffer() = _buffer.buffer().right(_buffer.size() - _buffer.pos()); + _buffer.seek(0); } } diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 08fd19f57e..aba39dd0c3 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -167,6 +167,32 @@ private: QHash _reliableInputChannels; }; +/// A list of contiguous spans, alternating between set and unset. +class SpanList { +public: + + SpanList(); + + int getTotalSet() const { return _totalSet; } + + /// Sets a region of the list. + /// \return the set length at the beginning of the list + int set(int offset, int length); + +private: + + class Span { + public: + int unset; + int set; + }; + + int setSpans(QList::iterator it, int length); + + QList _spans; + int _totalSet; +}; + /// Represents a single reliable channel multiplexed onto the datagram sequence. class ReliableChannel : public QObject { Q_OBJECT @@ -193,7 +219,7 @@ private: friend class DatagramSequencer; - ReliableChannel(DatagramSequencer* sequencer, int index); + ReliableChannel(DatagramSequencer* sequencer, int index, bool output); void writeData(QDataStream& out, int bytes, QVector& spans); void writeSpan(QDataStream& out, int position, int length, QVector& spans); @@ -204,12 +230,13 @@ private: int _index; QBuffer _buffer; + QByteArray _assemblyBuffer; QDataStream _dataStream; Bitstream _bitstream; float _priority; int _offset; - int _acknowledged; + SpanList _acknowledged; }; #endif /* defined(__interface__DatagramSequencer__) */ From f6320d3a8193d21b4b0ba86d78746cae28b8603f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Feb 2014 12:27:46 -0800 Subject: [PATCH 21/24] I think this covers the basic bits necessary for the reliable streaming. Of course, I haven't tested it yet. --- .../metavoxels/src/DatagramSequencer.cpp | 107 +++++++++++++++--- libraries/metavoxels/src/DatagramSequencer.h | 33 ++++-- 2 files changed, 113 insertions(+), 27 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index d372383f0d..ee218d7927 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -312,7 +312,7 @@ SpanList::SpanList() : _totalSet(0) { } int SpanList::set(int offset, int length) { - // see if it intersects the front of the list + // if we intersect the front of the list, consume beginning spans and return advancement if (offset <= 0) { int intersection = offset + length; return (intersection > 0) ? setSpans(_spans.begin(), intersection) : 0; @@ -321,15 +321,36 @@ int SpanList::set(int offset, int length) { // look for an intersection within the list int position = 0; for (QList::iterator it = _spans.begin(); it != _spans.end(); it++) { + // if we intersect the unset portion, contract it position += it->unset; if (offset <= position) { + int remove = position - offset; + it->unset -= remove; + // if we continue into the set portion, expand it and consume following spans + int extra = offset + length - position; + if (extra >= 0) { + int amount = setSpans(it + 1, extra); + it->set += amount; + _totalSet += amount; + + // otherwise, insert a new span + } else { + Span span = { it->unset, length + extra }; + _spans.insert(it, span); + it->unset = -extra; + _totalSet += span.set; + } return 0; } + // if we intersect the set portion, expand it and consume following spans position += it->set; if (offset <= position) { - + int extra = offset + length - position; + int amount = setSpans(it + 1, extra); + it->set += amount; + _totalSet += amount; return 0; } } @@ -337,6 +358,7 @@ int SpanList::set(int offset, int length) { // add to end of list Span span = { offset - position, length }; _spans.append(span); + _totalSet += length; return 0; } @@ -354,6 +376,7 @@ int SpanList::setSpans(QList::iterator it, int length) { remainingLength = qMax(remainingLength - combined, 0); totalRemoved += combined; it = _spans.erase(it); + _totalSet -= it->set; } return qMax(length, totalRemoved); } @@ -377,7 +400,8 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o _dataStream(&_buffer), _bitstream(_dataStream), _priority(1.0f), - _offset(0) { + _offset(0), + _writePosition(0) { _buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); @@ -386,25 +410,78 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o } void ReliableChannel::writeData(QDataStream& out, int bytes, QVector& spans) { - // nothing for now - out << (quint32)0; + // find out how many spans we want to write + int spanCount = 0; + int remainingBytes = bytes; + bool first = true; + while (remainingBytes > 0) { + int position = 0; + foreach (const SpanList::Span& span, _acknowledged.getSpans()) { + if (remainingBytes <= 0) { + break; + } + spanCount++; + + remainingBytes -= getBytesToWrite(first, span.unset); + position += (span.unset + span.set); + } + int leftover = _buffer.pos() - position; + if (remainingBytes > 0 && leftover > 0) { + spanCount++; + remainingBytes -= getBytesToWrite(first, leftover); + } + } + + // write the count and the spans + out << (quint32)spanCount; + remainingBytes = bytes; + first = true; + while (remainingBytes > 0) { + int position = 0; + foreach (const SpanList::Span& span, _acknowledged.getSpans()) { + if (remainingBytes <= 0) { + break; + } + remainingBytes -= writeSpan(out, first, position, qMin(remainingBytes, span.unset), spans); + position += (span.unset + span.set); + } + if (remainingBytes > 0 && position < _buffer.pos()) { + remainingBytes -= writeSpan(out, first, position, qMin(remainingBytes, (int)(_buffer.pos() - position)), spans); + } + } } -void ReliableChannel::writeSpan(QDataStream& out, int position, int length, QVector& spans) { +int ReliableChannel::getBytesToWrite(bool& first, int length) const { + if (first) { + first = false; + return length - (_writePosition % length); + } + return length; +} + +int ReliableChannel::writeSpan(QDataStream& out, bool& first, int position, int length, QVector& spans) { + if (first) { + first = false; + position = _writePosition % length; + length -= position; + _writePosition += length; + } DatagramSequencer::ChannelSpan span = { _index, _offset + position, length }; spans.append(span); out << (quint32)span.offset; out << (quint32)length; out.writeRawData(_buffer.data().constData() + position, length); + return length; } void ReliableChannel::spanAcknowledged(const DatagramSequencer::ChannelSpan& span) { - int acknowledged = _acknowledged.set(span.offset - _offset, span.length); - if (acknowledged > 0) { + int advancement = _acknowledged.set(span.offset - _offset, span.length); + if (advancement > 0) { // TODO: better way of pruning buffer - _buffer.buffer() = _buffer.buffer().right(_buffer.size() - acknowledged); + _buffer.buffer() = _buffer.buffer().right(_buffer.size() - advancement); _buffer.seek(_buffer.size()); - _offset += acknowledged; + _offset += advancement; + _writePosition = qMax(_writePosition - advancement, 0); } } @@ -428,13 +505,13 @@ void ReliableChannel::readData(QDataStream& in) { } else { in.readRawData(_assemblyBuffer.data() + position, size); } - int acknowledged = _acknowledged.set(position, size); - if (acknowledged > 0) { + int advancement = _acknowledged.set(position, size); + if (advancement > 0) { // TODO: better way of pruning buffer - _buffer.buffer().append(_assemblyBuffer.constData(), acknowledged); + _buffer.buffer().append(_assemblyBuffer.constData(), advancement); emit _buffer.readyRead(); - _assemblyBuffer = _assemblyBuffer.right(_assemblyBuffer.size() - acknowledged); - _offset += acknowledged; + _assemblyBuffer = _assemblyBuffer.right(_assemblyBuffer.size() - advancement); + _offset += advancement; } } diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index aba39dd0c3..44d3c83116 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -167,26 +167,33 @@ private: QHash _reliableInputChannels; }; -/// A list of contiguous spans, alternating between set and unset. +/// A list of contiguous spans, alternating between set and unset. Conceptually, the list is preceeded by a set +/// span of infinite length and followed by an unset span of infinite length. Within those bounds, it alternates +/// between unset and set. class SpanList { public: - SpanList(); - - int getTotalSet() const { return _totalSet; } - - /// Sets a region of the list. - /// \return the set length at the beginning of the list - int set(int offset, int length); - -private: - class Span { public: int unset; int set; }; + SpanList(); + + const QList& getSpans() const { return _spans; } + + /// Returns the total length set. + int getTotalSet() const { return _totalSet; } + + /// Sets a region of the list. + /// \return the advancement of the set length at the beginning of the list + int set(int offset, int length); + +private: + + /// Sets the spans starting at the specified iterator, consuming at least the given length. + /// \return the actual amount set, which may be greater if we ran into an existing set span int setSpans(QList::iterator it, int length); QList _spans; @@ -222,7 +229,8 @@ private: ReliableChannel(DatagramSequencer* sequencer, int index, bool output); void writeData(QDataStream& out, int bytes, QVector& spans); - void writeSpan(QDataStream& out, int position, int length, QVector& spans); + int getBytesToWrite(bool& first, int length) const; + int writeSpan(QDataStream& out, bool& first, int position, int length, QVector& spans); void spanAcknowledged(const DatagramSequencer::ChannelSpan& span); @@ -236,6 +244,7 @@ private: float _priority; int _offset; + int _writePosition; SpanList _acknowledged; }; From be21eee704af9afd7f8869741c2f0da892e83428 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Feb 2014 15:41:21 -0800 Subject: [PATCH 22/24] More work on parameterized scripts. --- .../metavoxels/src/AttributeRegistry.cpp | 37 ++---- libraries/metavoxels/src/AttributeRegistry.h | 29 ----- libraries/metavoxels/src/Bitstream.cpp | 12 ++ libraries/metavoxels/src/Bitstream.h | 4 + libraries/metavoxels/src/MetavoxelUtil.cpp | 105 +++++++++++++++++- libraries/metavoxels/src/MetavoxelUtil.h | 61 ++++++++++ libraries/metavoxels/src/ScriptCache.cpp | 13 ++- libraries/metavoxels/src/ScriptCache.h | 2 + 8 files changed, 202 insertions(+), 61 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index fd5d742597..431eaf62c9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -6,10 +6,7 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include -#include #include -#include #include "AttributeRegistry.h" #include "MetavoxelData.h" @@ -29,12 +26,23 @@ AttributeRegistry::AttributeRegistry() : _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { } +static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { + QDebug debug = qDebug(); + + for (int i = 0; i < context->argumentCount(); i++) { + debug << context->argument(i).toString(); + } + + return QScriptValue(); +} + void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) { QScriptValue registry = engine->newObject(); registry.setProperty("colorAttribute", engine->newQObject(_colorAttribute.data())); registry.setProperty("normalAttribute", engine->newQObject(_normalAttribute.data())); registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1)); engine->globalObject().setProperty("AttributeRegistry", registry); + engine->globalObject().setProperty("qDebug", engine->newFunction(qDebugFunction, 1)); } AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) { @@ -162,32 +170,11 @@ void* QRgbAttribute::createFromVariant(const QVariant& value) const { } QWidget* QRgbAttribute::createEditor(QWidget* parent) const { - QRgbEditor* editor = new QRgbEditor(parent); + QColorEditor* editor = new QColorEditor(parent); editor->setColor(QColor::fromRgba(_defaultValue)); return editor; } -QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { - QVBoxLayout* layout = new QVBoxLayout(); - layout->setAlignment(Qt::AlignTop); - setLayout(layout); - layout->addWidget(_button = new QPushButton()); - connect(_button, SIGNAL(clicked()), SLOT(selectColor())); -} - -void QRgbEditor::setColor(const QColor& color) { - QString name = (_color = color).name(); - _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name())); - _button->setText(name); -} - -void QRgbEditor::selectColor() { - QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel); - if (color.isValid()) { - setColor(color); - } -} - 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 677b3b4dee..db5e54cc4a 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -9,11 +9,8 @@ #ifndef __interface__AttributeRegistry__ #define __interface__AttributeRegistry__ -#include -#include #include #include -#include #include #include #include @@ -21,9 +18,6 @@ #include "Bitstream.h" #include "SharedObject.h" -class QComboBox; -class QFormLayout; -class QPushButton; class QScriptContext; class QScriptEngine; class QScriptValue; @@ -256,29 +250,6 @@ public: virtual QWidget* createEditor(QWidget* parent = NULL) const; }; -/// Editor for RGBA values. -class QRgbEditor : public QWidget { - Q_OBJECT - Q_PROPERTY(QColor color MEMBER _color WRITE setColor USER true) - -public: - - QRgbEditor(QWidget* parent); - -public slots: - - void setColor(const QColor& color); - -private slots: - - void selectColor(); - -private: - - QPushButton* _button; - QColor _color; -}; - /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 390d9f8f30..c7b4758a26 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -24,6 +24,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) +REGISTER_SIMPLE_TYPE_STREAMER(QColor) REGISTER_SIMPLE_TYPE_STREAMER(QString) REGISTER_SIMPLE_TYPE_STREAMER(QUrl) REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) @@ -246,6 +247,17 @@ Bitstream& Bitstream::operator>>(QByteArray& string) { return read(string.data(), size * BITS_IN_BYTE); } +Bitstream& Bitstream::operator<<(const QColor& color) { + return *this << (int)color.rgba(); +} + +Bitstream& Bitstream::operator>>(QColor& color) { + int rgba; + *this >> rgba; + color.setRgba(rgba); + return *this; +} + Bitstream& Bitstream::operator<<(const QString& string) { *this << string.size(); return write(string.constData(), string.size() * sizeof(QChar) * BITS_IN_BYTE); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index fe8b296f61..5a79b10766 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -21,6 +21,7 @@ #include "SharedObject.h" class QByteArray; +class QColor; class QDataStream; class QUrl; @@ -247,6 +248,9 @@ public: Bitstream& operator<<(const QByteArray& string); Bitstream& operator>>(QByteArray& string); + Bitstream& operator<<(const QColor& color); + Bitstream& operator>>(QColor& color); + Bitstream& operator<<(const QString& string); Bitstream& operator>>(QString& string); diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 0d3b5fc441..decfb7d917 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -7,6 +7,7 @@ // #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +83,18 @@ static QItemEditorCreatorBase* createDoubleEditorCreator() { return creator; } +static QItemEditorCreatorBase* createQColorEditorCreator() { + QItemEditorCreatorBase* creator = new QStandardItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + +static QItemEditorCreatorBase* createVec3EditorCreator() { + QItemEditorCreatorBase* creator = new QStandardItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { QItemEditorCreatorBase* creator = new QStandardItemEditorCreator(); getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); @@ -88,6 +102,8 @@ static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { } static QItemEditorCreatorBase* doubleEditorCreator = createDoubleEditorCreator(); +static QItemEditorCreatorBase* qColorEditorCreator = createQColorEditorCreator(); +static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator(); static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) { @@ -110,6 +126,63 @@ bool Box::contains(const Box& other) const { other.minimum.z >= minimum.z && other.maximum.z <= maximum.z; } +QColorEditor::QColorEditor(QWidget* parent) : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(); + layout->setContentsMargins(QMargins()); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + layout->addWidget(_button = new QPushButton()); + connect(_button, SIGNAL(clicked()), SLOT(selectColor())); +} + +void QColorEditor::setColor(const QColor& color) { + QString name = (_color = color).name(); + _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name())); + _button->setText(name); +} + +void QColorEditor::selectColor() { + QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel); + if (color.isValid()) { + setColor(color); + emit colorChanged(color); + } +} + +Vec3Editor::Vec3Editor(QWidget* parent) : QWidget(parent) { + QHBoxLayout* layout = new QHBoxLayout(); + layout->setContentsMargins(QMargins()); + setLayout(layout); + + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + layout->addWidget(_x = new QDoubleSpinBox()); + _x->setMinimum(-FLT_MAX); + _x->setMaximumWidth(100); + connect(_x, SIGNAL(valueChanged(double)), SLOT(updateVector())); + + layout->addWidget(_y = new QDoubleSpinBox()); + _y->setMinimum(-FLT_MAX); + _y->setMaximumWidth(100); + connect(_y, SIGNAL(valueChanged(double)), SLOT(updateVector())); + + layout->addWidget(_z = new QDoubleSpinBox()); + _z->setMinimum(-FLT_MAX); + _z->setMaximumWidth(100); + connect(_z, SIGNAL(valueChanged(double)), SLOT(updateVector())); +} + +void Vec3Editor::setVector(const glm::vec3& vector) { + _vector = vector; + _x->setValue(vector.x); + _y->setValue(vector.y); + _z->setValue(vector.z); +} + +void Vec3Editor::updateVector() { + emit vectorChanged(_vector = glm::vec3(_x->value(), _y->value(), _z->value())); +} + ParameterizedURL::ParameterizedURL(const QUrl& url, const ScriptHash& parameters) : _url(url), _parameters(parameters) { @@ -172,8 +245,19 @@ void ParameterizedURLEditor::setURL(const ParameterizedURL& url) { } void ParameterizedURLEditor::updateURL() { - _url = ParameterizedURL(_line->text()); - emit urlChanged(_url); + ScriptHash parameters; + if (layout()->count() > 1) { + QFormLayout* form = static_cast(layout()->itemAt(1)); + for (int i = 0; i < form->rowCount(); i++) { + QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); + QByteArray valuePropertyName = widget->property("valuePropertyName").toByteArray(); + const QMetaObject* widgetMetaObject = widget->metaObject(); + QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); + parameters.insert(ScriptCache::getInstance()->getEngine()->toStringHandle( + widget->property("parameterName").toString()), widgetProperty.read(widget)); + } + } + emit urlChanged(_url = ParameterizedURL(_line->text(), parameters)); if (_program) { _program->disconnect(this); } @@ -212,10 +296,19 @@ void ParameterizedURLEditor::continueUpdatingParameters() { QFormLayout* form = new QFormLayout(); layout->addLayout(form); foreach (const ParameterInfo& parameter, parameters) { - QWidget* editor = QItemEditorFactory::defaultFactory()->createEditor(parameter.type, NULL); - if (editor) { - form->addRow(parameter.name.toString() + ":", editor); - + QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(parameter.type, NULL); + if (widget) { + form->addRow(parameter.name.toString() + ":", widget); + QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(parameter.type); + widget->setProperty("parameterName", parameter.name.toString()); + widget->setProperty("valuePropertyName", valuePropertyName); + const QMetaObject* widgetMetaObject = widget->metaObject(); + QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); + widgetProperty.write(widget, _url.getParameters().value(parameter.name)); + if (widgetProperty.hasNotifySignal()) { + connect(widget, QByteArray(SIGNAL()).append(widgetProperty.notifySignal().methodSignature()), + SLOT(updateURL())); + } } } } diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index ad3084fe56..e62008ada0 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -9,15 +9,20 @@ #ifndef __interface__MetavoxelUtil__ #define __interface__MetavoxelUtil__ +#include #include #include #include #include +#include + #include "Bitstream.h" class QByteArray; +class QDoubleSpinBox; class QLineEdit; +class QPushButton; class HifiSockAddr; class NetworkProgram; @@ -41,6 +46,62 @@ public: DECLARE_STREAMABLE_METATYPE(Box) +/// Editor for color values. +class QColorEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged USER true) + +public: + + QColorEditor(QWidget* parent); + +signals: + + void colorChanged(const QColor& color); + +public slots: + + void setColor(const QColor& color); + +private slots: + + void selectColor(); + +private: + + QPushButton* _button; + QColor _color; +}; + +/// Editor for vector values. +class Vec3Editor : public QWidget { + Q_OBJECT + Q_PROPERTY(glm::vec3 vector MEMBER _vector WRITE setVector NOTIFY vectorChanged USER true) + +public: + + Vec3Editor(QWidget* parent); + +signals: + + void vectorChanged(const glm::vec3& vector); + +public slots: + + void setVector(const glm::vec3& vector); + +private slots: + + void updateVector(); + +private: + + QDoubleSpinBox* _x; + QDoubleSpinBox* _y; + QDoubleSpinBox* _z; + glm::vec3 _vector; +}; + typedef QHash ScriptHash; Q_DECLARE_METATYPE(ScriptHash) diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index e99418c5c6..e7610038f5 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -159,8 +159,19 @@ DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer& bas QScriptValue& DerivedNetworkValue::getValue() { if (!_value.isValid() && _baseValue->isLoaded()) { - _value = _baseValue->getValue(); + RootNetworkValue* root = static_cast(_baseValue.data()); + ScriptCache* cache = root->getProgram()->getCache(); + QScriptValue generator = _baseValue->getValue().property(cache->getGeneratorString()); + if (generator.isFunction()) { + QScriptValueList arguments; + foreach (const ParameterInfo& parameter, root->getParameterInfo()) { + arguments.append(cache->getEngine()->newVariant(_parameters.value(parameter.name))); + } + _value = generator.call(QScriptValue(), arguments); + } else { + _value = _baseValue->getValue(); + } } return _value; } diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index 1c51380c3f..3ce525d979 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -130,6 +130,8 @@ public: RootNetworkValue(const QSharedPointer& program); + const QSharedPointer& getProgram() const { return _program; } + virtual QScriptValue& getValue(); const QList& getParameterInfo(); From d7df4173e5c0d91badbe32970deb48bf6322f101 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Feb 2014 18:58:36 -0800 Subject: [PATCH 23/24] Cleaner component creation. --- libraries/metavoxels/src/MetavoxelUtil.cpp | 27 +++++++++------------- libraries/metavoxels/src/MetavoxelUtil.h | 2 ++ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index decfb7d917..e762fd06e0 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -154,22 +154,9 @@ Vec3Editor::Vec3Editor(QWidget* parent) : QWidget(parent) { layout->setContentsMargins(QMargins()); setLayout(layout); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - layout->addWidget(_x = new QDoubleSpinBox()); - _x->setMinimum(-FLT_MAX); - _x->setMaximumWidth(100); - connect(_x, SIGNAL(valueChanged(double)), SLOT(updateVector())); - - layout->addWidget(_y = new QDoubleSpinBox()); - _y->setMinimum(-FLT_MAX); - _y->setMaximumWidth(100); - connect(_y, SIGNAL(valueChanged(double)), SLOT(updateVector())); - - layout->addWidget(_z = new QDoubleSpinBox()); - _z->setMinimum(-FLT_MAX); - _z->setMaximumWidth(100); - connect(_z, SIGNAL(valueChanged(double)), SLOT(updateVector())); + layout->addWidget(_x = createComponentBox()); + layout->addWidget(_y = createComponentBox()); + layout->addWidget(_z = createComponentBox()); } void Vec3Editor::setVector(const glm::vec3& vector) { @@ -183,6 +170,14 @@ void Vec3Editor::updateVector() { emit vectorChanged(_vector = glm::vec3(_x->value(), _y->value(), _z->value())); } +QDoubleSpinBox* Vec3Editor::createComponentBox() { + QDoubleSpinBox* box = new QDoubleSpinBox(); + box->setMinimum(-FLT_MAX); + box->setMaximumWidth(100); + connect(box, SIGNAL(valueChanged(double)), SLOT(updateVector())); + return box; +} + ParameterizedURL::ParameterizedURL(const QUrl& url, const ScriptHash& parameters) : _url(url), _parameters(parameters) { diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index e62008ada0..117825be64 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -96,6 +96,8 @@ private slots: private: + QDoubleSpinBox* createComponentBox(); + QDoubleSpinBox* _x; QDoubleSpinBox* _y; QDoubleSpinBox* _z; From d3b005112ebc767875a38126972d8656d1acb5c1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 7 Feb 2014 10:03:23 -0800 Subject: [PATCH 24/24] Changes from Philip's code review. --- libraries/metavoxels/src/Bitstream.cpp | 2 +- libraries/metavoxels/src/DatagramSequencer.cpp | 8 ++++++-- libraries/octree/src/OctreePacketData.h | 2 +- libraries/octree/src/OctreeSceneStats.h | 1 + libraries/shared/src/Logging.cpp | 2 +- libraries/shared/src/NetworkPacket.cpp | 2 +- libraries/shared/src/NetworkPacket.h | 2 +- libraries/shared/src/NodeList.h | 2 -- libraries/shared/src/OctalCode.h | 1 - libraries/shared/src/PacketSender.h | 1 + libraries/shared/src/SharedUtil.h | 4 ++++ 11 files changed, 17 insertions(+), 10 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index c7b4758a26..262f2df7f5 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "AttributeRegistry.h" #include "Bitstream.h" @@ -96,7 +97,6 @@ Bitstream::Bitstream(QDataStream& underlying, QObject* parent) : _sharedObjectStreamer(*this) { } -const int BITS_IN_BYTE = 8; const int LAST_BIT_POSITION = BITS_IN_BYTE - 1; Bitstream& Bitstream::write(const void* data, int bits, int offset) { diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index e941d9e4d3..af271d97ba 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -10,10 +10,13 @@ #include +#include + #include "DatagramSequencer.h" #include "MetavoxelMessages.h" -const int MAX_DATAGRAM_SIZE = 1500; +// in sequencer parlance, a "packet" may consist of multiple datagrams. clarify when we refer to actual datagrams +const int MAX_DATAGRAM_SIZE = MAX_PACKET_SIZE; const int DEFAULT_MAX_PACKET_SIZE = 3000; @@ -515,7 +518,8 @@ void ReliableChannel::readData(QDataStream& in) { } } - // TODO: better way of pruning buffer? + // when the read head is sufficiently advanced into the buffer, prune it off. this along + // with other buffer usages should be replaced with a circular buffer const int PRUNE_SIZE = 8192; if (_buffer.pos() > PRUNE_SIZE) { _buffer.buffer() = _buffer.buffer().right(_buffer.size() - _buffer.pos()); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 76bb138516..a71a3f893c 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -224,4 +224,4 @@ private: static quint64 _totalBytesOfRawData; }; -#endif /* defined(__hifi__OctreePacketData__) */ \ No newline at end of file +#endif /* defined(__hifi__OctreePacketData__) */ diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index bf65e85ad4..e106f53589 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -12,6 +12,7 @@ #include #include +#include #include "JurisdictionMap.h" #define GREENISH 0x40ff40d0 diff --git a/libraries/shared/src/Logging.cpp b/libraries/shared/src/Logging.cpp index 6fb716e638..bc0d4af084 100644 --- a/libraries/shared/src/Logging.cpp +++ b/libraries/shared/src/Logging.cpp @@ -126,4 +126,4 @@ void Logging::verboseMessageHandler(QtMsgType type, const QMessageLogContext& co } fprintf(stdout, "%s %s\n", prefixString.toLocal8Bit().constData(), message.toLocal8Bit().constData()); -} \ No newline at end of file +} diff --git a/libraries/shared/src/NetworkPacket.cpp b/libraries/shared/src/NetworkPacket.cpp index aacd95dbbf..d0f0614088 100644 --- a/libraries/shared/src/NetworkPacket.cpp +++ b/libraries/shared/src/NetworkPacket.cpp @@ -48,4 +48,4 @@ NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) { copyContents(other.getAddress(), other.getByteArray()); return *this; } -#endif \ No newline at end of file +#endif diff --git a/libraries/shared/src/NetworkPacket.h b/libraries/shared/src/NetworkPacket.h index 480a793a49..c5a54b7939 100644 --- a/libraries/shared/src/NetworkPacket.h +++ b/libraries/shared/src/NetworkPacket.h @@ -20,7 +20,7 @@ #include "HifiSockAddr.h" -#include "NodeList.h" // for MAX_PACKET_SIZE +#include "SharedUtil.h" // for MAX_PACKET_SIZE /// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet class NetworkPacket { diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 6b8acd6e99..56e433a86f 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -30,8 +30,6 @@ #include "Node.h" -const int MAX_PACKET_SIZE = 1500; - const quint64 NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000; const quint64 DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000; const quint64 PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000; diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 34d4264de4..36f3e74f63 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -12,7 +12,6 @@ #include #include -const int BITS_IN_BYTE = 8; const int BITS_IN_OCTAL = 3; const int NUMBER_OF_COLORS = 3; // RGB! const int SIZE_OF_COLOR_DATA = NUMBER_OF_COLORS * sizeof(unsigned char); // size in bytes diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h index 4a1e62bd0c..83a146a096 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -13,6 +13,7 @@ #include "GenericThread.h" #include "NetworkPacket.h" +#include "NodeList.h" #include "SharedUtil.h" /// Generalized threaded processor for queueing and sending of outbound packets. diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index c982ab3596..603a09a0c3 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -61,6 +61,10 @@ static const quint64 USECS_PER_MSEC = 1000; static const quint64 MSECS_PER_SECOND = 1000; static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; +const int BITS_IN_BYTE = 8; + +const int MAX_PACKET_SIZE = 1500; + quint64 usecTimestamp(const timeval *time); quint64 usecTimestampNow(); void usecTimestampNowForceClockSkew(int clockSkew);