From 60b0281095b0172d60c22da6ec25076f49e70714 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Mar 2014 17:12:02 -0800 Subject: [PATCH] Ordered visitation bits. --- interface/src/MetavoxelSystem.cpp | 33 +++-- interface/src/MetavoxelSystem.h | 10 +- libraries/metavoxels/src/MetavoxelData.cpp | 119 +++++++++++++----- libraries/metavoxels/src/MetavoxelData.h | 45 +++++-- .../metavoxels/src/MetavoxelMessages.cpp | 18 +-- 5 files changed, 162 insertions(+), 63 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4c1b40dc23..b6b4c2b668 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -61,13 +61,14 @@ void MetavoxelSystem::simulate(float deltaTime) { // simulate the clients _points.clear(); _simulateVisitor.setDeltaTime(deltaTime); + _simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection()); foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { client->simulate(deltaTime); - client->getData().guide(_simulateVisitor); + client->guide(_simulateVisitor); } } } @@ -109,7 +110,7 @@ void MetavoxelSystem::render() { glEnableClientState(GL_NORMAL_ARRAY); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - + glDrawArrays(GL_POINTS, 0, _points.size()); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); @@ -127,7 +128,7 @@ void MetavoxelSystem::render() { QMutexLocker locker(&node->getMutex()); MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { - client->getData().guide(_renderVisitor); + client->guide(_renderVisitor); } } } @@ -147,15 +148,16 @@ MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : _points(points) { } -void MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) { +bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) { spanner->getRenderer()->simulate(_deltaTime); + return true; } -bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { +int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { SpannerVisitor::visit(info); if (!info.isLeaf) { - return true; + return _order; } QRgb color = info.inputValues.at(0).getInlineValue(); QRgb normal = info.inputValues.at(1).getInlineValue(); @@ -165,15 +167,16 @@ bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { { qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } }; _points.append(point); } - return false; + return STOP_RECURSION; } MetavoxelSystem::RenderVisitor::RenderVisitor() : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()) { } -void MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { +bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { spanner->getRenderer()->render(1.0f); + return true; } MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : @@ -201,6 +204,16 @@ MetavoxelClient::~MetavoxelClient() { _sequencer.endPacket(); } +static MetavoxelLOD getLOD() { + const float FIXED_LOD_THRESHOLD = 0.01f; + return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD); +} + +void MetavoxelClient::guide(MetavoxelVisitor& visitor) { + visitor.setLOD(getLOD()); + _data.guide(visitor); +} + void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { // apply immediately to local tree edit.apply(_data); @@ -211,8 +224,8 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); - const float FIXED_LOD_THRESHOLD = 0.0001f; - ClientStateMessage state = { MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD) }; + + ClientStateMessage state = { getLOD() }; out << QVariant::fromValue(state); _sequencer.endPacket(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 155f99ffbd..c1833832a7 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -57,18 +57,20 @@ private: public: SimulateVisitor(QVector& points); void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; } - virtual void visit(Spanner* spanner); - virtual bool visit(MetavoxelInfo& info); + void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); } + virtual bool visit(Spanner* spanner); + virtual int visit(MetavoxelInfo& info); private: QVector& _points; float _deltaTime; + int _order; }; class RenderVisitor : public SpannerVisitor { public: RenderVisitor(); - virtual void visit(Spanner* spanner); + virtual bool visit(Spanner* spanner); }; static ProgramObject _program; @@ -89,7 +91,7 @@ public: MetavoxelClient(const SharedNodePointer& node); virtual ~MetavoxelClient(); - MetavoxelData& getData() { return _data; } + void guide(MetavoxelVisitor& visitor); void applyEdit(const MetavoxelEditMessage& edit); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index f3cce11e75..18a290d6e2 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -32,7 +32,19 @@ bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size) const { } bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference) const { - return shouldSubdivide(minimum, size) && !reference.shouldSubdivide(minimum, size); + if (position == reference.position && threshold >= reference.threshold) { + return false; // first off, nothing becomes subdivided if it doesn't change + } + if (!shouldSubdivide(minimum, size)) { + return false; // this one must be subdivided + } + // the general check is whether we've gotten closer (as multiplied by the threshold) to any point in the volume, + // which we approximate as a sphere for simplicity + float halfSize = size * 0.5f; + glm::vec3 center = minimum + glm::vec3(halfSize, halfSize, halfSize); + float radius = sqrtf(3 * halfSize * halfSize); + return qMax(0.0f, glm::distance(position, center) - radius) * threshold <= + qMax(0.0f, glm::distance(reference.position, center) - radius) * reference.threshold; } MetavoxelData::MetavoxelData() : _size(1.0f) { @@ -128,7 +140,7 @@ public: SpannerUpdateVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); private: @@ -147,17 +159,17 @@ template SpannerUpdateVisitor::SpannerUpdateVisitor( _object(object) { } -template bool SpannerUpdateVisitor::visit(MetavoxelInfo& info) { +template int SpannerUpdateVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { - return false; + return STOP_RECURSION; } if (info.size > _longestSide) { - return true; + return DEFAULT_ORDER; } SharedObjectSet set = info.inputValues.at(0).getInlineValue(); F(set, _object); info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); - return false; + return STOP_RECURSION; } void MetavoxelData::insert(const AttributePointer& attribute, const SharedObjectPointer& object) { @@ -737,9 +749,43 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) { } } -MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, const QVector& outputs) : +int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, + int fifth, int sixth, int seventh, int eighth) { + return first | (second << 3) | (third << 6) | (fourth << 9) | + (fifth << 12) | (sixth << 15) | (seventh << 18) | (eighth << 21); +} + +class IndexDistance { +public: + int index; + float distance; +}; + +bool operator<(const IndexDistance& first, const IndexDistance& second) { + return first.distance < second.distance; +} + +int MetavoxelVisitor::encodeOrder(const glm::vec3& direction) { + QList indexDistances; + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + IndexDistance indexDistance = { i, glm::dot(direction, getNextMinimum(glm::vec3(), 1.0f, i)) }; + indexDistances.append(indexDistance); + } + qStableSort(indexDistances); + return encodeOrder(indexDistances.at(0).index, indexDistances.at(1).index, indexDistances.at(2).index, + indexDistances.at(3).index, indexDistances.at(4).index, indexDistances.at(5).index, + indexDistances.at(6).index, indexDistances.at(7).index); +} + +const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7); +const int MetavoxelVisitor::STOP_RECURSION = 0; +const int MetavoxelVisitor::SHORT_CIRCUIT = -1; + +MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod) : _inputs(inputs), - _outputs(outputs) { + _outputs(outputs), + _lod(lod) { } MetavoxelVisitor::~MetavoxelVisitor() { @@ -750,8 +796,8 @@ void MetavoxelVisitor::prepare() { } SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs) : - MetavoxelVisitor(inputs + spannerInputs, outputs), + const QVector& outputs, const MetavoxelLOD& lod) : + MetavoxelVisitor(inputs + spannerInputs, outputs, lod), _spannerInputCount(spannerInputs.size()) { } @@ -759,24 +805,30 @@ void SpannerVisitor::prepare() { Spanner::incrementVisit(); } -bool SpannerVisitor::visit(MetavoxelInfo& info) { +int SpannerVisitor::visit(MetavoxelInfo& info) { for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { - visit(spanner); + if (!visit(spanner)) { + return SHORT_CIRCUIT; + } } } } - return !info.isLeaf; + return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; } DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } -void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - visitation.info.isLeaf = visitation.allInputNodesLeaves(); - bool keepGoing = visitation.visitor.visit(visitation.info); +bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { + bool shouldSubdivide = visitation.visitor.getLOD().shouldSubdivide(visitation.info.minimum, visitation.info.size); + visitation.info.isLeaf = !shouldSubdivide || visitation.allInputNodesLeaves(); + int encodedOrder = visitation.visitor.visit(visitation.info); + if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { + return false; + } for (int i = 0; i < visitation.outputNodes.size(); i++) { OwnedAttributeValue& value = visitation.info.outputValues[i]; if (!value.getAttribute()) { @@ -790,29 +842,35 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { node = new MetavoxelNode(value); } } - if (!keepGoing) { - return; + if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { + return true; } MetavoxelVisitation nextVisitation = { &visitation, visitation.visitor, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + const int ORDER_ELEMENT_BITS = 3; + const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1; + int index = encodedOrder & ORDER_ELEMENT_MASK; + encodedOrder >>= ORDER_ELEMENT_BITS; for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); - MetavoxelNode* child = node ? node->getChild(i) : NULL; + MetavoxelNode* child = (node && shouldSubdivide) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : visitation.info.inputValues[j]; } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = node ? node->getChild(i) : NULL; + MetavoxelNode* child = (node && shouldSubdivide) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } - nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, i); - static_cast(nextVisitation.info.inputValues.last().getInlineValue< - SharedObjectPointer>().data())->guide(nextVisitation); + nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index); + if (!static_cast(nextVisitation.info.inputValues.last().getInlineValue< + SharedObjectPointer>().data())->guide(nextVisitation)) { + return false; + } for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { OwnedAttributeValue& value = nextVisitation.info.outputValues[j]; if (!value.getAttribute()) { @@ -839,10 +897,10 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // it's a leaf; we need to split it up AttributeValue nodeValue = node->getAttributeValue(value.getAttribute()); for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) { - node->setChild((i + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); + node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); } } - node->setChild(i, nextVisitation.outputNodes.at(j)); + node->setChild(index, nextVisitation.outputNodes.at(j)); value = AttributeValue(); } } @@ -854,12 +912,13 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { value = node->getAttributeValue(value.getAttribute()); } } + return true; } ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(10.0) { } -void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { +bool ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute(); for (int i = 0; i < visitation.info.inputValues.size(); i++) { AttributeValue& attributeValue = visitation.info.inputValues[i]; @@ -872,7 +931,7 @@ void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } - DefaultMetavoxelGuide::guide(visitation); + return DefaultMetavoxelGuide::guide(visitation); } static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, @@ -933,7 +992,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin ScriptedMetavoxelGuide::ScriptedMetavoxelGuide() { } -void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { +bool ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QScriptValue guideFunction; if (_guideFunction) { guideFunction = _guideFunction->getValue(); @@ -944,8 +1003,7 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } if (!guideFunction.isValid()) { // before we load, just use the default behavior - DefaultMetavoxelGuide::guide(visitation); - return; + return DefaultMetavoxelGuide::guide(visitation); } QScriptEngine* engine = guideFunction.engine(); if (!_minimumHandle.isValid()) { @@ -983,6 +1041,7 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { if (engine->hasUncaughtException()) { qDebug() << "Script error: " << engine->uncaughtException().toString(); } + return true; } void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 0c11055b0f..48246bb633 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -194,8 +194,24 @@ public: class MetavoxelVisitor { public: + /// Encodes a visitation order sequence for the children of a metavoxel. + static int encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth); + + /// Encodes a visitation order sequence that visits each child as sorted along the specified direction. + static int encodeOrder(const glm::vec3& direction); + + /// The default visitation order. + static const int DEFAULT_ORDER; + + /// A special "order" that instructs the guide to stop recursion. + static const int STOP_RECURSION; + + /// A special "order" that short-circuits the tour. + static const int SHORT_CIRCUIT; + MetavoxelVisitor(const QVector& inputs, - const QVector& outputs = QVector()); + const QVector& outputs = QVector(), + const MetavoxelLOD& lod = MetavoxelLOD()); virtual ~MetavoxelVisitor(); /// Returns a reference to the list of input attributes desired. @@ -204,18 +220,24 @@ public: /// Returns a reference to the list of output attributes provided. const QVector& getOutputs() const { return _outputs; } + /// Returns a reference to the level of detail that will determine subdivision levels. + const MetavoxelLOD& getLOD() const { return _lod; } + + void setLOD(const MetavoxelLOD& lod) { _lod = lod; } + /// Prepares for a new tour of the metavoxel data. virtual void prepare(); /// Visits a metavoxel. /// \param info the metavoxel data - /// \return if true, continue descending; if false, stop - virtual bool visit(MetavoxelInfo& info) = 0; + /// \return the encoded order in which to traverse the children, zero to stop recursion, or -1 to short-circuit the tour + virtual int visit(MetavoxelInfo& info) = 0; protected: QVector _inputs; QVector _outputs; + MetavoxelLOD _lod; }; typedef QSharedPointer MetavoxelVisitorPointer; @@ -226,13 +248,15 @@ public: SpannerVisitor(const QVector& spannerInputs, const QVector& inputs = QVector(), - const QVector& outputs = QVector()); + const QVector& outputs = QVector(), + const MetavoxelLOD& lod = MetavoxelLOD()); /// Visits a spanner. - virtual void visit(Spanner* spanner) = 0; + /// \return true to continue, false to short-circuit the tour + virtual bool visit(Spanner* spanner) = 0; virtual void prepare(); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); protected: @@ -246,7 +270,8 @@ class MetavoxelGuide : public SharedObject { public: /// Guides the specified visitor to the contained voxels. - virtual void guide(MetavoxelVisitation& visitation) = 0; + /// \return true to keep going, false to short circuit the tour + virtual bool guide(MetavoxelVisitation& visitation) = 0; }; /// Guides visitors through the explicit content of the system. @@ -257,7 +282,7 @@ public: Q_INVOKABLE DefaultMetavoxelGuide(); - virtual void guide(MetavoxelVisitation& visitation); + virtual bool guide(MetavoxelVisitation& visitation); }; /// A temporary test guide that just makes the existing voxels throb with delight. @@ -269,7 +294,7 @@ public: Q_INVOKABLE ThrobbingMetavoxelGuide(); - virtual void guide(MetavoxelVisitation& visitation); + virtual bool guide(MetavoxelVisitation& visitation); private: @@ -285,7 +310,7 @@ public: Q_INVOKABLE ScriptedMetavoxelGuide(); - virtual void guide(MetavoxelVisitation& visitation); + virtual bool guide(MetavoxelVisitation& visitation); public slots: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 7ee5482179..bcb59dad77 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -24,7 +24,7 @@ public: BoxSetEditVisitor(const BoxSetEdit& edit); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); private: @@ -36,26 +36,26 @@ BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : _edit(edit) { } -bool BoxSetEditVisitor::visit(MetavoxelInfo& info) { +int BoxSetEditVisitor::visit(MetavoxelInfo& info) { // find the intersection between volume and voxel glm::vec3 minimum = glm::max(info.minimum, _edit.region.minimum); glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.region.maximum); glm::vec3 size = maximum - minimum; if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { - return false; // disjoint + return STOP_RECURSION; // disjoint } float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); if (volume >= 1.0f) { info.outputValues[0] = _edit.value; - return false; // entirely contained + return STOP_RECURSION; // entirely contained } if (info.size <= _edit.granularity) { if (volume >= 0.5f) { info.outputValues[0] = _edit.value; } - return false; // reached granularity limit; take best guess + return STOP_RECURSION; // reached granularity limit; take best guess } - return true; // subdivide + return DEFAULT_ORDER; // subdivide } void BoxSetEdit::apply(MetavoxelData& data) const { @@ -77,7 +77,7 @@ public: GlobalSetEditVisitor(const GlobalSetEdit& edit); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); private: @@ -89,9 +89,9 @@ GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) : _edit(edit) { } -bool GlobalSetEditVisitor::visit(MetavoxelInfo& info) { +int GlobalSetEditVisitor::visit(MetavoxelInfo& info) { info.outputValues[0] = _edit.value; - return false; // entirely contained + return STOP_RECURSION; // entirely contained } void GlobalSetEdit::apply(MetavoxelData& data) const {