diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 61f7b55968..1259be3083 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -100,6 +100,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new InsertSpannerTool(this)); addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); + addTool(new SetSpannerTool(this)); updateAttributes(); @@ -529,15 +530,15 @@ void GlobalSetTool::apply() { Application::getInstance()->getMetavoxels()->applyEdit(message); } -InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Insert Spanner") { +PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText) : + MetavoxelTool(editor, name) { - QPushButton* button = new QPushButton("Insert"); + QPushButton* button = new QPushButton(placeText); layout()->addWidget(button); - connect(button, SIGNAL(clicked()), SLOT(insert())); + connect(button, SIGNAL(clicked()), SLOT(place())); } -void InsertSpannerTool::simulate(float deltaTime) { +void PlaceSpannerTool::simulate(float deltaTime) { if (Application::getInstance()->isMouseHidden()) { return; } @@ -558,7 +559,7 @@ void InsertSpannerTool::simulate(float deltaTime) { spanner->getRenderer()->simulate(deltaTime); } -void InsertSpannerTool::render() { +void PlaceSpannerTool::render() { if (Application::getInstance()->isMouseHidden()) { return; } @@ -567,28 +568,36 @@ void InsertSpannerTool::render() { spanner->getRenderer()->render(SPANNER_ALPHA); } -bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { +bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { return attribute->inherits("SpannerSetAttribute"); } -bool InsertSpannerTool::eventFilter(QObject* watched, QEvent* event) { +bool PlaceSpannerTool::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::MouseButtonPress) { - insert(); + place(); return true; } return false; } -void InsertSpannerTool::insert() { +void PlaceSpannerTool::place() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (!attribute) { return; } SharedObjectPointer spanner = _editor->getValue().value(); - MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) }; + MetavoxelEditMessage message = { createEdit(attribute, spanner) }; Application::getInstance()->getMetavoxels()->applyEdit(message); } +InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : + PlaceSpannerTool(editor, "Insert Spanner", "Insert") { +} + +QVariant InsertSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { + return QVariant::fromValue(InsertSpannerEdit(attribute, spanner)); +} + RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : MetavoxelTool(editor, "Remove Spanner", false) { } @@ -625,3 +634,16 @@ void ClearSpannersTool::clear() { MetavoxelEditMessage message = { QVariant::fromValue(ClearSpannersEdit(attribute)) }; Application::getInstance()->getMetavoxels()->applyEdit(message); } + +SetSpannerTool::SetSpannerTool(MetavoxelEditor* editor) : + PlaceSpannerTool(editor, "Set Spanner", "Set") { +} + +bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const { + return attribute == AttributeRegistry::getInstance()->getSpannersAttribute(); +} + +QVariant SetSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { + static_cast(spanner.data())->setGranularity(_editor->getGridSpacing()); + return QVariant::fromValue(SetSpannerEdit(spanner)); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 5b580129a9..76ef8baf6f 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -139,13 +139,13 @@ private slots: void apply(); }; -/// Allows inserting a spanner into the scene. -class InsertSpannerTool : public MetavoxelTool { +/// Base class for insert/set spanner tools. +class PlaceSpannerTool : public MetavoxelTool { Q_OBJECT public: - InsertSpannerTool(MetavoxelEditor* editor); + PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText); virtual void simulate(float deltaTime); @@ -155,9 +155,26 @@ public: virtual bool eventFilter(QObject* watched, QEvent* event); +protected: + + virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0; + private slots: - void insert(); + void place(); +}; + +/// Allows inserting a spanner into the scene. +class InsertSpannerTool : public PlaceSpannerTool { + Q_OBJECT + +public: + + InsertSpannerTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); }; /// Allows removing a spanner from the scene. @@ -188,4 +205,19 @@ private slots: void clear(); }; +/// Allows setting the value by placing a spanner. +class SetSpannerTool : public PlaceSpannerTool { + Q_OBJECT + +public: + + SetSpannerTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +protected: + + virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); +}; + #endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 18a290d6e2..f1c0b928c9 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -493,6 +493,7 @@ void MetavoxelNode::read(MetavoxelStreamState& state) { _children[i] = new MetavoxelNode(state.attribute); _children[i]->read(nextState); } + mergeChildren(state.attribute); } } @@ -549,7 +550,8 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta } } } - } + } + mergeChildren(state.attribute); } } @@ -1085,6 +1087,15 @@ void Spanner::setBounds(const Box& bounds) { emit boundsChanged(_bounds = bounds); } +const QVector& Spanner::getAttributes() const { + static QVector emptyVector; + return emptyVector; +} + +bool Spanner::getAttributeValues(MetavoxelInfo& info) const { + return false; +} + bool Spanner::testAndSetVisited() { if (_lastVisit == _visit) { return false; @@ -1164,6 +1175,43 @@ void Sphere::setColor(const QColor& color) { } } +const QVector& Sphere::getAttributes() const { + static QVector attributes = QVector() << + AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute(); + return attributes; +} + +bool Sphere::getAttributeValues(MetavoxelInfo& info) const { + // bounds check + Box bounds = info.getBounds(); + if (!getBounds().intersects(bounds)) { + return false; + } + // count the points inside the sphere + int pointsWithin = 0; + for (int i = 0; i < Box::VERTEX_COUNT; i++) { + if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { + pointsWithin++; + } + } + if (pointsWithin == Box::VERTEX_COUNT) { + // entirely contained + info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); + getNormal(info); + return false; + } + if (info.size <= getGranularity()) { + // best guess + if (pointsWithin > 0) { + info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(qRgba( + _color.red(), _color.green(), _color.blue(), _color.alpha() * pointsWithin / Box::VERTEX_COUNT))); + getNormal(info); + } + return false; + } + return true; +} + QByteArray Sphere::getRendererClassName() const { return "SphereRenderer"; } @@ -1173,6 +1221,24 @@ void Sphere::updateBounds() { setBounds(Box(getTranslation() - extent, getTranslation() + extent)); } +void Sphere::getNormal(MetavoxelInfo& info) const { + glm::vec3 normal = info.getCenter() - getTranslation(); + float length = glm::length(normal); + QRgb color; + if (length > EPSILON) { + const float NORMAL_SCALE = 127.0f; + float scale = NORMAL_SCALE / length; + const int BYTE_MASK = 0xFF; + color = qRgb((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK, + (int)(normal.z * scale) & BYTE_MASK); + + } else { + const QRgb DEFAULT_NORMAL = 0x007F00; + color = DEFAULT_NORMAL; + } + info.outputValues[1] = AttributeValue(getAttributes().at(1), encodeInline(color)); +} + StaticModel::StaticModel() { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 48246bb633..5b149c071f 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -188,6 +188,7 @@ public: bool isLeaf; Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } + glm::vec3 getCenter() const { return minimum + glm::vec3(size, size, size) * 0.5f; } }; /// Interface for visitors to metavoxels. @@ -374,6 +375,13 @@ public: void setGranularity(float granularity) { _granularity = granularity; } float getGranularity() const { return _granularity; } + /// Returns a reference to the list of attributes associated with this spanner. + virtual const QVector& getAttributes() const; + + /// Sets the attribute values associated with this spanner in the supplied info. + /// \return true to recurse, false to stop + virtual bool getAttributeValues(MetavoxelInfo& info) const; + /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(); @@ -459,6 +467,9 @@ public: void setColor(const QColor& color); const QColor& getColor() const { return _color; } + virtual const QVector& getAttributes() const; + virtual bool getAttributeValues(MetavoxelInfo& info) const; + signals: void colorChanged(const QColor& color); @@ -473,6 +484,8 @@ private slots: private: + void getNormal(MetavoxelInfo& info) const; + QColor _color; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index bcb59dad77..1c36f7df46 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -123,3 +123,40 @@ ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : void ClearSpannersEdit::apply(MetavoxelData& data) const { data.clear(attribute); } + +class SetSpannerEditVisitor : public MetavoxelVisitor { +public: + + SetSpannerEditVisitor(Spanner* spanner); + + virtual int visit(MetavoxelInfo& info); + +private: + + Spanner* _spanner; +}; + +SetSpannerEditVisitor::SetSpannerEditVisitor(Spanner* spanner) : + MetavoxelVisitor(QVector(), spanner->getAttributes()), + _spanner(spanner) { +} + +int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { + return _spanner->getAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; +} + +SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : + spanner(spanner) { +} + +void SetSpannerEdit::apply(MetavoxelData& data) const { + Spanner* spanner = static_cast(this->spanner.data()); + + // expand to fit the entire spanner + while (!data.getBounds().contains(spanner->getBounds())) { + data.expand(); + } + + SetSpannerEditVisitor visitor(spanner); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 980d198872..fe229cc727 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -161,4 +161,19 @@ public: DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) +/// An edit that sets a spanner's attributes in the voxel tree. +class SetSpannerEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM SharedObjectPointer spanner; + + SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer()); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(SetSpannerEdit) + #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 86cbe87fd8..9e96b43d3f 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -162,6 +162,17 @@ bool Box::intersects(const Box& other) const { other.maximum.z >= minimum.z && other.minimum.z <= maximum.z; } +const int X_MAXIMUM_FLAG = 1; +const int Y_MAXIMUM_FLAG = 2; +const int Z_MAXIMUM_FLAG = 4; + +glm::vec3 Box::getVertex(int index) const { + return glm::vec3( + (index & X_MAXIMUM_FLAG) ? maximum.x : minimum.x, + (index & Y_MAXIMUM_FLAG) ? maximum.y : minimum.y, + (index & Z_MAXIMUM_FLAG) ? maximum.z : minimum.z); +} + Box operator*(const glm::mat4& matrix, const Box& box) { // start with the constant component Box newBox(glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]), glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2])); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index adb106b652..45a9476e4d 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -34,6 +34,8 @@ class Box { public: + static const int VERTEX_COUNT = 8; + STREAM glm::vec3 minimum; STREAM glm::vec3 maximum; @@ -44,6 +46,10 @@ public: bool intersects(const Box& other) const; float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); } + + glm::vec3 getVertex(int index) const; + + glm::vec3 getCenter() const { return (minimum + maximum) * 0.5f; } }; DECLARE_STREAMABLE_METATYPE(Box)