From 005ae79928fe74c6febd0777dbdaaed7c636e73a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 31 Mar 2014 18:21:28 -0700 Subject: [PATCH] Various fixes and what-not, got the basic spanner clipping working. --- interface/src/MetavoxelSystem.cpp | 63 ++++++- interface/src/MetavoxelSystem.h | 35 +++- interface/src/ui/MetavoxelEditor.cpp | 2 +- .../metavoxels/src/AttributeRegistry.cpp | 8 +- libraries/metavoxels/src/AttributeRegistry.h | 37 ++++ libraries/metavoxels/src/Bitstream.cpp | 6 +- libraries/metavoxels/src/MetavoxelData.cpp | 166 +++++++++++++++--- libraries/metavoxels/src/MetavoxelData.h | 36 +++- .../metavoxels/src/MetavoxelMessages.cpp | 75 ++++++-- 9 files changed, 363 insertions(+), 65 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 2da21aae7e..042c9329f2 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -167,14 +167,14 @@ void MetavoxelSystem::maybeAttachClient(const SharedNodePointer& node) { MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector() << AttributeRegistry::getInstance()->getColorAttribute() << + QVector(), QVector() << AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute() << AttributeRegistry::getInstance()->getSpannerColorAttribute() << AttributeRegistry::getInstance()->getSpannerNormalAttribute()), _points(points) { } -bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) { +bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { spanner->getRenderer()->simulate(_deltaTime); return true; } @@ -223,11 +223,12 @@ int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { } MetavoxelSystem::RenderVisitor::RenderVisitor() : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()) { + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector() << AttributeRegistry::getInstance()->getSpannerMaskAttribute()) { } -bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { - spanner->getRenderer()->render(1.0f); +bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { + spanner->getRenderer()->render(1.0f, clipMinimum, clipSize); return true; } @@ -333,10 +334,55 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } +static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { + GLdouble coefficients[] = { x, y, z, w }; + glClipPlane(plane, coefficients); + glEnable(plane); +} + +void ClippedRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) { + if (clipSize == 0.0f) { + renderUnclipped(alpha); + return; + } + enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize); + enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); + enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipSize); + enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); + enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize); + enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z); + + renderUnclipped(alpha); + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + glDisable(GL_CLIP_PLANE4); + glDisable(GL_CLIP_PLANE5); +} + SphereRenderer::SphereRenderer() { } -void SphereRenderer::render(float alpha) { +void SphereRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) { + if (clipSize == 0.0f) { + renderUnclipped(alpha); + return; + } + // slight performance optimization: don't render if clip bounds are entirely within sphere + Sphere* sphere = static_cast(parent()); + Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); + for (int i = 0; i < Box::VERTEX_COUNT; i++) { + const float CLIP_PROPORTION = 0.95f; + if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) { + ClippedRenderer::render(alpha, clipMinimum, clipSize); + return; + } + } +} + +void SphereRenderer::renderUnclipped(float alpha) { Sphere* sphere = static_cast(parent()); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha); @@ -384,11 +430,12 @@ void StaticModelRenderer::simulate(float deltaTime) { _model->simulate(deltaTime); } -void StaticModelRenderer::render(float alpha) { +void StaticModelRenderer::renderUnclipped(float alpha) { _model->render(alpha); } -bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return _model->findRayIntersection(origin, direction, distance); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index ea3f4ed18f..58f40dad37 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -61,7 +61,7 @@ private: SimulateVisitor(QVector& points); void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; } void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); } - virtual bool visit(Spanner* spanner); + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); virtual int visit(MetavoxelInfo& info); private: @@ -73,7 +73,7 @@ private: class RenderVisitor : public SpannerVisitor { public: RenderVisitor(); - virtual bool visit(Spanner* spanner); + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); }; static ProgramObject _program; @@ -141,19 +141,36 @@ private: QList _receiveRecords; }; +/// Base class for spanner renderers; provides clipping. +class ClippedRenderer : public SpannerRenderer { + Q_OBJECT + +public: + + virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize); + +protected: + + virtual void renderUnclipped(float alpha) = 0; +}; + /// Renders spheres. -class SphereRenderer : public SpannerRenderer { +class SphereRenderer : public ClippedRenderer { Q_OBJECT public: Q_INVOKABLE SphereRenderer(); - virtual void render(float alpha); + virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize); + +protected: + + virtual void renderUnclipped(float alpha); }; /// Renders static models. -class StaticModelRenderer : public SpannerRenderer { +class StaticModelRenderer : public ClippedRenderer { Q_OBJECT public: @@ -162,8 +179,12 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(float alpha); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; + +protected: + + virtual void renderUnclipped(float alpha); private slots: diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 7a70cb9a4c..19f5902336 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -565,7 +565,7 @@ void PlaceSpannerTool::render() { } Spanner* spanner = static_cast(_editor->getValue().value().data()); const float SPANNER_ALPHA = 0.25f; - spanner->getRenderer()->render(SPANNER_ALPHA); + spanner->getRenderer()->render(SPANNER_ALPHA, glm::vec3(), 0.0f); } bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index ec0cf06dc6..fc448613f9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -11,6 +11,7 @@ #include "AttributeRegistry.h" #include "MetavoxelData.h" +REGISTER_META_OBJECT(FloatAttribute) REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(SpannerQRgbAttribute) @@ -34,7 +35,8 @@ AttributeRegistry::AttributeRegistry() : _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), _spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))), - _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))) { + _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))), + _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))) { // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -198,6 +200,10 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt root.writeSubdivision(state); } +FloatAttribute::FloatAttribute(const QString& name, float defaultValue) : + SimpleInlineAttribute(name, defaultValue) { +} + QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : InlineAttribute(name, defaultValue) { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 28a110467c..f7d8d955a5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -80,6 +80,9 @@ public: /// Returns a reference to the standard packed normal "spannerNormal" attribute. const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; } + /// Returns a reference to the standard "spannerMask" attribute. + const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -91,6 +94,7 @@ private: AttributePointer _normalAttribute; AttributePointer _spannerColorAttribute; AttributePointer _spannerNormalAttribute; + AttributePointer _spannerMaskAttribute; }; /// Converts a value to a void pointer. @@ -265,6 +269,39 @@ template inline void InlineAttribute::write(Bitstrea } } +/// Provides averaging using the +=, ==, and / operators. +template class SimpleInlineAttribute : public InlineAttribute { +public: + + SimpleInlineAttribute(const QString& name, const T& defaultValue = T()) : InlineAttribute(name, defaultValue) { } + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + +template inline bool SimpleInlineAttribute::merge( + void*& parent, void* children[], bool postRead) const { + T firstValue = decodeInline(children[0]); + T totalValue = firstValue; + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + T value = decodeInline(children[i]); + totalValue += value; + allChildrenEqual &= (firstValue == value); + } + parent = encodeInline(totalValue / Attribute::MERGE_COUNT); + return allChildrenEqual; +} + +/// Simple float attribute. +class FloatAttribute : public SimpleInlineAttribute { + Q_OBJECT + Q_PROPERTY(float defaultValue MEMBER _defaultValue) + +public: + + Q_INVOKABLE FloatAttribute(const QString& name = QString(), float defaultValue = 0.0f); +}; + /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 059a457e5e..077e6c1c69 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -223,12 +223,12 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { if (!it.value()) { continue; } - QPointer& reference = _sharedObjectReferences[it.value()->getID()]; + QPointer& reference = _sharedObjectReferences[it.value()->getRemoteID()]; if (reference) { _sharedObjectStreamer.removePersistentValue(reference.data()); } reference = it.value(); - _weakSharedObjectHash.remove(it.value()->getID()); + _weakSharedObjectHash.remove(it.value()->getRemoteID()); } } @@ -863,7 +863,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } } else { QObject* rawObject; - if (reference) { + if (reference) { readRawDelta(rawObject, (QObject*)reference.data()); } else { *this >> rawObject; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index aae4d7761f..e548de46c7 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -212,6 +212,67 @@ void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds, guide(visitor); } +void MetavoxelData::replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, + const SharedObjectPointer& newObject) { + Spanner* spanner = static_cast(oldObject.data()); + replace(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), oldObject, newObject); +} + +class SpannerReplaceVisitor : public MetavoxelVisitor { +public: + + SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); + + virtual int visit(MetavoxelInfo& info); + +private: + + const AttributePointer& _attribute; + const Box& _bounds; + float _longestSide; + const SharedObjectPointer& _oldObject; + const SharedObjectPointer& _newObject; +}; + +SpannerReplaceVisitor::SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, + const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) : + MetavoxelVisitor(QVector() << attribute, QVector() << attribute), + _attribute(attribute), + _bounds(bounds), + _longestSide(qMax(bounds.getLongestSide(), granularity)), + _oldObject(oldObject), + _newObject(newObject) { +} + +int SpannerReplaceVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + if (info.size > _longestSide) { + return DEFAULT_ORDER; + } + SharedObjectSet set = info.inputValues.at(0).getInlineValue(); + if (set.remove(_oldObject)) { + set.insert(_newObject); + } + info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); + return STOP_RECURSION; +} + +void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity, + const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) { + Spanner* newSpanner = static_cast(newObject.data()); + if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) { + // if the bounds have changed, we must remove and reinsert + remove(attribute, bounds, granularity, oldObject); + insert(attribute, newSpanner->getBounds(), newSpanner->getPlacementGranularity(), newObject); + return; + } + SpannerReplaceVisitor visitor(attribute, bounds, granularity, oldObject, newObject); + guide(visitor); +} + void MetavoxelData::clear(const AttributePointer& attribute) { MetavoxelNode* node = _roots.take(attribute); if (node) { @@ -239,7 +300,7 @@ private: FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) : RaySpannerIntersectionVisitor(origin, direction, QVector() << attribute, - QVector(), QVector(), lod), + QVector(), QVector(), QVector(), lod), _spanner(NULL) { } @@ -878,10 +939,11 @@ void MetavoxelVisitor::prepare() { // nothing by default } -SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod) : - MetavoxelVisitor(inputs + spannerInputs, outputs, lod), - _spannerInputCount(spannerInputs.size()) { +SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& spannerMasks, + const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : + MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod), + _spannerInputCount(spannerInputs.size()), + _spannerMaskCount(spannerMasks.size()) { } void SpannerVisitor::prepare() { @@ -889,17 +951,34 @@ void SpannerVisitor::prepare() { } int SpannerVisitor::visit(MetavoxelInfo& info) { - for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { + for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (spanner->testAndSetVisited()) { - if (!visit(spanner)) { - return SHORT_CIRCUIT; - } + if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited() && + !visit(spanner, glm::vec3(), 0.0f)) { + return SHORT_CIRCUIT; } } } - return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { + float maskValue = info.inputValues.at(i).getInlineValue(); + if (maskValue < 0.5f) { + const MetavoxelInfo* nextInfo = &info; + do { + foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( + i - _spannerInputCount).getInlineValue()) { + Spanner* spanner = static_cast(object.data()); + if (spanner->isMasked() && !visit(spanner, info.minimum, info.size)) { + return SHORT_CIRCUIT; + } + } + } while ((nextInfo = nextInfo->parentInfo)); + } + } + return STOP_RECURSION; } RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, @@ -919,10 +998,11 @@ int RayIntersectionVisitor::visit(MetavoxelInfo& info) { } RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), - _spannerInputCount(spannerInputs.size()) { + const QVector& spannerInputs, const QVector& spannerMasks, + const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, inputs + spannerInputs + spannerMasks, outputs, lod), + _spannerInputCount(spannerInputs.size()), + _spannerMaskCount(spannerMasks.size()) { } void RaySpannerIntersectionVisitor::prepare() { @@ -941,12 +1021,12 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; - for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { + for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (spanner->testAndSetVisited()) { + if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited()) { SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) { + if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) { spannerDistances.append(spannerDistance); } } @@ -958,7 +1038,36 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { } } } - return info.isLeaf ? STOP_RECURSION : _order; + if (!info.isLeaf) { + return _order; + } + for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { + float maskValue = info.inputValues.at(i).getInlineValue(); + if (maskValue < 0.5f) { + const MetavoxelInfo* nextInfo = &info; + do { + foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( + i - _spannerInputCount).getInlineValue()) { + Spanner* spanner = static_cast(object.data()); + if (spanner->isMasked()) { + SpannerDistance spannerDistance = { spanner }; + if (spanner->findRayIntersection(_origin, _direction, + info.minimum, info.size, spannerDistance.distance)) { + spannerDistances.append(spannerDistance); + } + } + } + } while ((nextInfo = nextInfo->parentInfo)); + + qStableSort(spannerDistances); + foreach (const SpannerDistance& spannerDistance, spannerDistances) { + if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { + return SHORT_CIRCUIT; + } + } + } + } + return STOP_RECURSION; } DefaultMetavoxelGuide::DefaultMetavoxelGuide() { @@ -1224,6 +1333,7 @@ Spanner::Spanner() : _renderer(NULL), _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), + _masked(false), _lastVisit(0) { } @@ -1276,7 +1386,8 @@ SpannerRenderer* Spanner::getRenderer() { return _renderer; } -bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return _bounds.findRayIntersection(origin, direction, distance); } @@ -1297,11 +1408,12 @@ void SpannerRenderer::simulate(float deltaTime) { // nothing by default } -void SpannerRenderer::render(float alpha) { +void SpannerRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) { // nothing by default } -bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return false; } @@ -1424,7 +1536,8 @@ bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const { return true; } -bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); } @@ -1463,10 +1576,11 @@ void StaticModel::setURL(const QUrl& url) { } } -bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const { // delegate to renderer, if we have one - return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : - Spanner::findRayIntersection(origin, direction, distance); + return _renderer ? _renderer->findRayIntersection(origin, direction, clipMinimum, clipSize, distance) : + Spanner::findRayIntersection(origin, direction, clipMinimum, clipSize, distance); } QByteArray StaticModel::getRendererClassName() const { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index fd7ce437cc..fa408aafb7 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -79,6 +79,11 @@ public: void toggle(const AttributePointer& attribute, const SharedObjectPointer& object); void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, + const SharedObjectPointer& newObject); + void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, + const SharedObjectPointer& newObject); + void clear(const AttributePointer& attribute); /// Convenience function that finds the first spanner intersecting the provided ray. @@ -255,13 +260,15 @@ class SpannerVisitor : public MetavoxelVisitor { public: SpannerVisitor(const QVector& spannerInputs, + const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD()); - /// Visits a spanner. + /// Visits a spanner (or part thereof). + /// \param clipSize the size of the clip volume, or zero if unclipped /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner) = 0; + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0; virtual void prepare(); virtual int visit(MetavoxelInfo& info); @@ -269,6 +276,7 @@ public: protected: int _spannerInputCount; + int _spannerMaskCount; }; /// Base class for ray intersection visitors. @@ -298,11 +306,12 @@ public: RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const QVector& spannerInputs, + const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD()); - /// Visits a spanner that the ray intersects. + /// Visits a spannerthat the ray intersects. /// \return true to continue, false to short-circuit the tour virtual bool visitSpanner(Spanner* spanner, float distance) = 0; @@ -312,6 +321,7 @@ public: protected: int _spannerInputCount; + int _spannerMaskCount; }; /// Interface for objects that guide metavoxel visitors. @@ -412,6 +422,7 @@ class Spanner : public SharedObject { Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) + Q_PROPERTY(float masked MEMBER _masked DESIGNABLE false) public: @@ -429,6 +440,9 @@ public: void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } float getVoxelizationGranularity() const { return _voxelizationGranularity; } + void setMasked(bool masked) { _masked = masked; } + bool isMasked() const { return _masked; } + /// Returns a reference to the list of attributes associated with this spanner. virtual const QVector& getAttributes() const; @@ -452,7 +466,9 @@ public: SpannerRenderer* getRenderer(); /// Finds the intersection between the described ray and this spanner. - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + /// \param clipSize the size of the clip region, or zero if unclipped + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; signals: @@ -471,6 +487,7 @@ private: Box _bounds; float _placementGranularity; float _voxelizationGranularity; + bool _masked; int _lastVisit; ///< the identifier of the last visit static int _visit; ///< the global visit counter @@ -486,8 +503,9 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(float alpha); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; }; /// An object with a 3D transform. @@ -539,7 +557,8 @@ public: virtual const QVector& getVoxelizedAttributes() const; virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize, float& distance) const; signals: @@ -572,7 +591,8 @@ public: void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& clipMinimum, float clipSize,float& distance) const; signals: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index ac4f8fc7f6..8ed97da54d 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -32,7 +32,8 @@ private: }; BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : - MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute() << + AttributeRegistry::getInstance()->getSpannerMaskAttribute()), _edit(edit) { } @@ -47,25 +48,68 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) { float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); if (volume >= 1.0f) { info.outputValues[0] = _edit.value; + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); return STOP_RECURSION; // entirely contained } if (info.size <= _edit.granularity) { if (volume >= 0.5f) { info.outputValues[0] = _edit.value; + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); } return STOP_RECURSION; // reached granularity limit; take best guess } return DEFAULT_ORDER; // subdivide } +class GatherUnmaskedSpannersVisitor : public SpannerVisitor { +public: + + GatherUnmaskedSpannersVisitor(const Box& bounds); + + const QList& getUnmaskedSpanners() const { return _unmaskedSpanners; } + + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + +private: + + Box _bounds; + QList _unmaskedSpanners; +}; + +GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), + _bounds(bounds) { +} + +bool GatherUnmaskedSpannersVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { + if (!spanner->isMasked() && spanner->getBounds().intersects(_bounds)) { + _unmaskedSpanners.append(spanner); + } + return true; +} + +static void setIntersectingMasked(const Box& bounds, MetavoxelData& data) { + GatherUnmaskedSpannersVisitor visitor(bounds); + data.guide(visitor); + + foreach (const SharedObjectPointer& object, visitor.getUnmaskedSpanners()) { + Spanner* newSpanner = static_cast(object->clone(true)); + newSpanner->setMasked(true); + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), object, newSpanner); + } +} + void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { data.expand(); } - BoxSetEditVisitor visitor(*this); - data.guide(visitor); + BoxSetEditVisitor setVisitor(*this); + data.guide(setVisitor); + + // flip the mask flag of all intersecting spanners + setIntersectingMasked(region, data); } GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) : @@ -180,25 +224,25 @@ ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : attribute(attribute) { } -class GetSpannerAttributesVisitor : public SpannerVisitor { +class GatherSpannerAttributesVisitor : public SpannerVisitor { public: - GetSpannerAttributesVisitor(const AttributePointer& attribute); + GatherSpannerAttributesVisitor(const AttributePointer& attribute); const QSet& getAttributes() const { return _attributes; } - virtual bool visit(Spanner* spanner); + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); protected: QSet _attributes; }; -GetSpannerAttributesVisitor::GetSpannerAttributesVisitor(const AttributePointer& attribute) : +GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) : SpannerVisitor(QVector() << attribute) { } -bool GetSpannerAttributesVisitor::visit(Spanner* spanner) { +bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) { _attributes.insert(attribute); } @@ -207,7 +251,7 @@ bool GetSpannerAttributesVisitor::visit(Spanner* spanner) { void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // find all the spanner attributes - GetSpannerAttributesVisitor visitor(attribute); + GatherSpannerAttributesVisitor visitor(attribute); data.guide(visitor); data.clear(attribute); @@ -233,12 +277,19 @@ private: }; SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(attributes, attributes), + MetavoxelVisitor(attributes, QVector() << attributes << + AttributeRegistry::getInstance()->getSpannerMaskAttribute()), _spanner(spanner) { } int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { - return _spanner->blendAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; + if (_spanner->blendAttributeValues(info)) { + return DEFAULT_ORDER; + } + if (info.outputValues.at(0).getAttribute()) { + info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline(1.0f)); + } + return STOP_RECURSION; } void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { @@ -251,4 +302,6 @@ void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& obje SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner); data.guide(visitor); + + setIntersectingMasked(spanner->getBounds(), data); }