Ordered visitation bits.

This commit is contained in:
Andrzej Kapolka 2014-03-05 17:12:02 -08:00
parent c4f29005a0
commit 60b0281095
5 changed files with 162 additions and 63 deletions

View file

@ -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<MetavoxelClient*>(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<MetavoxelClient*>(node->getLinkedData());
if (client) {
client->getData().guide(_renderVisitor);
client->guide(_renderVisitor);
}
}
}
@ -147,15 +148,16 @@ MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector<Point>& 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>();
QRgb normal = info.inputValues.at(1).getInlineValue<QRgb>();
@ -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<AttributePointer>() << 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();

View file

@ -57,18 +57,20 @@ private:
public:
SimulateVisitor(QVector<Point>& 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<Point>& _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);

View file

@ -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<SpannerUpdateFunction F> SpannerUpdateVisitor<F>::SpannerUpdateVisitor(
_object(object) {
}
template<SpannerUpdateFunction F> bool SpannerUpdateVisitor<F>::visit(MetavoxelInfo& info) {
template<SpannerUpdateFunction F> int SpannerUpdateVisitor<F>::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<SharedObjectSet>();
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<AttributePointer>& inputs, const QVector<AttributePointer>& 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<IndexDistance> 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<AttributePointer>& inputs,
const QVector<AttributePointer>& 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<AttributePointer>& spannerInputs, const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs) :
MetavoxelVisitor(inputs + spannerInputs, outputs),
const QVector<AttributePointer>& 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<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(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<MetavoxelNode*>(visitation.inputNodes.size()), QVector<MetavoxelNode*>(visitation.outputNodes.size()),
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.inputNodes.size()),
QVector<OwnedAttributeValue>(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<MetavoxelGuide*>(nextVisitation.info.inputValues.last().getInlineValue<
SharedObjectPointer>().data())->guide(nextVisitation);
nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index);
if (!static_cast<MetavoxelGuide*>(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) {

View file

@ -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<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs = QVector<AttributePointer>());
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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<AttributePointer>& 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<AttributePointer> _inputs;
QVector<AttributePointer> _outputs;
MetavoxelLOD _lod;
};
typedef QSharedPointer<MetavoxelVisitor> MetavoxelVisitorPointer;
@ -226,13 +248,15 @@ public:
SpannerVisitor(const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>());
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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:

View file

@ -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 {