diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index fc448613f9..8914a9e3c9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -144,6 +144,14 @@ void OwnedAttributeValue::mix(const AttributeValue& first, const AttributeValue& _value = _attribute->mix(first.getValue(), second.getValue(), alpha); } +void OwnedAttributeValue::blend(const AttributeValue& source, const AttributeValue& dest) { + if (_attribute) { + _attribute->destroy(_value); + } + _attribute = source.getAttribute(); + _value = _attribute->blend(source.getValue(), dest.getValue()); +} + OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) { if (_attribute) { _attribute->destroy(_value); @@ -243,6 +251,19 @@ void* QRgbAttribute::mix(void* first, void* second, float alpha) const { glm::mix((float)qAlpha(firstValue), (float)qAlpha(secondValue), alpha))); } +const float EIGHT_BIT_MAXIMUM = 255.0f; + +void* QRgbAttribute::blend(void* source, void* dest) const { + QRgb sourceValue = decodeInline(source); + QRgb destValue = decodeInline(dest); + float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM; + return encodeInline(qRgba( + glm::mix((float)qRed(destValue), (float)qRed(sourceValue), alpha), + glm::mix((float)qGreen(destValue), (float)qGreen(sourceValue), alpha), + glm::mix((float)qBlue(destValue), (float)qBlue(sourceValue), alpha), + glm::mix((float)qAlpha(destValue), (float)qAlpha(sourceValue), alpha))); +} + void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return encodeInline((QRgb)value.toUInt32()); } @@ -287,6 +308,13 @@ void* PackedNormalAttribute::mix(void* first, void* second, float alpha) const { return encodeInline(packNormal(glm::normalize(glm::mix(firstNormal, secondNormal, alpha)))); } +void* PackedNormalAttribute::blend(void* source, void* dest) const { + QRgb sourceValue = decodeInline(source); + QRgb destValue = decodeInline(dest); + float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM; + return encodeInline(packNormal(glm::normalize(glm::mix(unpackNormal(destValue), unpackNormal(sourceValue), alpha)))); +} + const float CHAR_SCALE = 127.0f; const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index f7d8d955a5..db13ea9f4e 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -158,6 +158,9 @@ public: /// Sets this attribute to a mix of the first and second provided. void mix(const AttributeValue& first, const AttributeValue& second, float alpha); + /// Sets this attribute to a blend of the source and destination. + void blend(const AttributeValue& source, const AttributeValue& dest); + /// Destroys the current value, if any, and copies the specified other value. OwnedAttributeValue& operator=(const AttributeValue& other); @@ -218,6 +221,9 @@ public: /// Mixes the first and the second, returning a new value with the result. virtual void* mix(void* first, void* second, float alpha) const = 0; + /// Blends the source with the destination, returning a new value with the result. + virtual void* blend(void* source, void* dest) const = 0; + virtual void* getDefaultValue() const = 0; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } @@ -249,6 +255,8 @@ public: virtual void* mix(void* first, void* second, float alpha) const { return create(alpha < 0.5f ? first : second); } + virtual void* blend(void* source, void* dest) const { return create(source); } + virtual void* getDefaultValue() const { return encodeInline(_defaultValue); } protected: @@ -315,6 +323,8 @@ public: virtual void* mix(void* first, void* second, float alpha) const; + virtual void* blend(void* source, void* dest) const; + virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; virtual void* createFromVariant(const QVariant& value) const; @@ -333,6 +343,8 @@ public: virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual void* mix(void* first, void* second, float alpha) const; + + virtual void* blend(void* source, void* dest) const; }; /// Packs a normal into an RGB value. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c89967b3a0..efbc0b157d 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -336,17 +336,41 @@ static glm::vec3 getNextMinimum(const glm::vec3& minimum, float nextSize, int in (index & Z_MAXIMUM_FLAG) ? nextSize : 0.0f); } -static void setNode(const AttributePointer& attribute, MetavoxelNode*& node, MetavoxelNode* other) { - if (node) { - node->decrementReferenceCount(attribute); +static void setNode(const AttributeValue& value, MetavoxelNode*& node, MetavoxelNode* other, bool blend) { + if (!blend) { + // if we're not blending, we can just make a shallow copy + if (node) { + node->decrementReferenceCount(value.getAttribute()); + } + (node = other)->incrementReferenceCount(); + return; } - (node = other)->incrementReferenceCount(); + if (node) { + MetavoxelNode* oldNode = node; + node = new MetavoxelNode(value.getAttribute(), oldNode); + oldNode->decrementReferenceCount(value.getAttribute()); + + } else { + node = new MetavoxelNode(value); + } + OwnedAttributeValue oldValue = node->getAttributeValue(value.getAttribute()); + node->blendAttributeValues(other->getAttributeValue(value.getAttribute()), oldValue); + if (other->isLeaf()) { + node->clearChildren(value.getAttribute()); + return; + } + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + MetavoxelNode* child = node->getChild(i); + setNode(oldValue, child, other->getChild(i), true); + node->setChild(i, child); + } + node->mergeChildren(value.getAttribute()); } static void setNode(const AttributeValue& value, MetavoxelNode*& node, const glm::vec3& minimum, float size, - MetavoxelNode* other, const glm::vec3& otherMinimum, float otherSize) { + MetavoxelNode* other, const glm::vec3& otherMinimum, float otherSize, bool blend) { if (otherSize >= size) { - setNode(value.getAttribute(), node, other); + setNode(value, node, other, blend); return; } if (!node) { @@ -372,21 +396,27 @@ static void setNode(const AttributeValue& value, MetavoxelNode*& node, const glm } MetavoxelNode* nextNode = node->getChild(index); setNode(node->getAttributeValue(value.getAttribute()), nextNode, getNextMinimum(minimum, nextSize, index), - nextSize, other, otherMinimum, otherSize); + nextSize, other, otherMinimum, otherSize, blend); node->setChild(index, nextNode); + node->mergeChildren(value.getAttribute()); } -void MetavoxelData::set(const glm::vec3& minimum, const MetavoxelData& data) { +void MetavoxelData::set(const glm::vec3& minimum, const MetavoxelData& data, bool blend) { // expand to fit the entire data Box bounds = minimum + glm::vec3(data.getSize(), data.getSize(), data.getSize()); while (!getBounds().contains(bounds)) { expand(); } - // set each attribute separately + // set/mix each attribute separately for (QHash::const_iterator it = data._roots.constBegin(); it != data._roots.constEnd(); it++) { - setNode(it.key(), _roots[it.key()], getMinimum(), getSize(), it.value(), minimum, data.getSize()); + MetavoxelNode*& root = _roots[it.key()]; + setNode(it.key(), root, getMinimum(), getSize(), it.value(), minimum, data.getSize(), blend); + if (root->isLeaf() && root->getAttributeValue(it.key()).isDefault()) { + _roots.remove(it.key()); + root->decrementReferenceCount(it.key()); + } } } @@ -659,7 +689,11 @@ MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelN void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) { attributeValue.getAttribute()->destroy(_attributeValue); _attributeValue = attributeValue.copy(); - clearChildren(attributeValue.getAttribute()); +} + +void MetavoxelNode::blendAttributeValues(const AttributeValue& source, const AttributeValue& dest) { + source.getAttribute()->destroy(_attributeValue); + _attributeValue = source.getAttribute()->blend(source.getValue(), dest.getValue()); } AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribute) const { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 4db2e919b4..ed8b1a224c 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -91,7 +91,7 @@ public: const AttributePointer& attribute, float& distance, const MetavoxelLOD& lod = MetavoxelLOD()); /// Sets part of the data. - void set(const glm::vec3& minimum, const MetavoxelData& data); + void set(const glm::vec3& minimum, const MetavoxelData& data, bool blend = false); /// Expands the tree, increasing its capacity in all dimensions. void expand(); @@ -158,6 +158,8 @@ public: void setAttributeValue(const AttributeValue& attributeValue); + void blendAttributeValues(const AttributeValue& source, const AttributeValue& dest); + AttributeValue getAttributeValue(const AttributePointer& attribute) const; void* getAttributeValue() const { return _attributeValue; } @@ -190,13 +192,13 @@ public: void destroy(const AttributePointer& attribute); + void clearChildren(const AttributePointer& attribute); + private: Q_DISABLE_COPY(MetavoxelNode) friend class MetavoxelVisitation; - void clearChildren(const AttributePointer& attribute); - int _referenceCount; void* _attributeValue; MetavoxelNode* _children[CHILD_COUNT]; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 7bf10467f6..c930688cbd 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -306,11 +306,12 @@ void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& obje setIntersectingMasked(spanner->getBounds(), data); } -SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data) : +SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) : minimum(minimum), - data(data) { + data(data), + blend(blend) { } void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.set(minimum, this->data); + data.set(minimum, this->data, blend); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 309439812d..4f90a52e4c 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -183,10 +183,10 @@ class SetDataEdit : public MetavoxelEdit { public: STREAM glm::vec3 minimum; - STREAM MetavoxelData data; + STREAM bool blend; - SetDataEdit(const glm::vec3& minimum = glm::vec3(), const MetavoxelData& data = MetavoxelData()); + SetDataEdit(const glm::vec3& minimum = glm::vec3(), const MetavoxelData& data = MetavoxelData(), bool blend = false); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; };