overte-HifiExperiments/libraries/metavoxels/src/MetavoxelMessages.cpp

486 lines
17 KiB
C++

//
// MetavoxelMessages.cpp
// libraries/metavoxels/src
//
// Created by Andrzej Kapolka on 1/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MetavoxelMessages.h"
void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
static_cast<const MetavoxelEdit*>(edit.data())->apply(data, objects);
}
MetavoxelEdit::~MetavoxelEdit() {
}
BoxSetEdit::BoxSetEdit(const Box& region, float granularity, const OwnedAttributeValue& value) :
region(region), granularity(granularity), value(value) {
}
class BoxSetEditVisitor : public MetavoxelVisitor {
public:
BoxSetEditVisitor(const BoxSetEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
const BoxSetEdit& _edit;
};
BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute() <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
_edit(edit) {
}
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 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;
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<float>(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<float>(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<SharedObjectPointer>& getUnmaskedSpanners() const { return _unmaskedSpanners; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
private:
Box _bounds;
QList<SharedObjectPointer> _unmaskedSpanners;
};
GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) :
SpannerVisitor(QVector<AttributePointer>() << 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<Spanner*>(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 setVisitor(*this);
data.guide(setVisitor);
// flip the mask flag of all intersecting spanners
setIntersectingMasked(region, data);
}
GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) :
value(value) {
}
class GlobalSetEditVisitor : public MetavoxelVisitor {
public:
GlobalSetEditVisitor(const GlobalSetEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
const GlobalSetEdit& _edit;
};
GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute()),
_edit(edit) {
}
int GlobalSetEditVisitor::visit(MetavoxelInfo& info) {
info.outputValues[0] = _edit.value;
return STOP_RECURSION; // entirely contained
}
void GlobalSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
GlobalSetEditVisitor visitor(*this);
data.guide(visitor);
}
InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) :
attribute(attribute),
spanner(spanner) {
}
class UpdateSpannerVisitor : public MetavoxelVisitor {
public:
UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
float _voxelizationSize;
int _steps;
};
UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(QVector<AttributePointer>() << attributes << AttributeRegistry::getInstance()->getSpannersAttribute(),
attributes),
_spanner(spanner),
_voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f /
AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()),
_steps(glm::round(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) /
logf(2.0f) - 2.0f)) {
}
int UpdateSpannerVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_spanner->getBounds())) {
return STOP_RECURSION;
}
MetavoxelInfo* parentInfo = info.parentInfo;
for (int i = 0; i < _steps && parentInfo; i++) {
parentInfo = parentInfo->parentInfo;
}
for (int i = 0; i < _outputs.size(); i++) {
info.outputValues[i] = AttributeValue(_outputs.at(i));
}
if (parentInfo) {
foreach (const SharedObjectPointer& object,
parentInfo->inputValues.at(_outputs.size()).getInlineValue<SharedObjectSet>()) {
static_cast<const Spanner*>(object.data())->blendAttributeValues(info, true);
}
}
return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION;
}
void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.insert(attribute, this->spanner);
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner);
data.guide(visitor);
}
RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) :
attribute(attribute),
id(id) {
}
void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
SharedObject* object = objects.value(id);
if (!object) {
qDebug() << "Missing object to remove" << id;
return;
}
// keep a strong reference to the object
SharedObjectPointer sharedPointer = object;
data.remove(attribute, object);
Spanner* spanner = static_cast<Spanner*>(object);
UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner);
data.guide(visitor);
}
ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) :
attribute(attribute) {
}
class GatherSpannerAttributesVisitor : public SpannerVisitor {
public:
GatherSpannerAttributesVisitor(const AttributePointer& attribute);
const QSet<AttributePointer>& getAttributes() const { return _attributes; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
protected:
QSet<AttributePointer> _attributes;
};
GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) :
SpannerVisitor(QVector<AttributePointer>() << attribute) {
}
bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) {
_attributes.insert(attribute);
}
return true;
}
void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// find all the spanner attributes
GatherSpannerAttributesVisitor visitor(attribute);
data.guide(visitor);
data.clear(attribute);
foreach (const AttributePointer& attribute, visitor.getAttributes()) {
data.clear(attribute);
}
}
SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) :
spanner(spanner) {
}
class SetSpannerEditVisitor : public MetavoxelVisitor {
public:
SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
};
SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(attributes, QVector<AttributePointer>() << attributes <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
_spanner(spanner) {
}
int SetSpannerEditVisitor::visit(MetavoxelInfo& info) {
if (_spanner->blendAttributeValues(info)) {
return DEFAULT_ORDER;
}
if (info.outputValues.at(0).getAttribute()) {
info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline<float>(1.0f));
}
return STOP_RECURSION;
}
void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
// expand to fit the entire spanner
while (!data.getBounds().contains(spanner->getBounds())) {
data.expand();
}
SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner);
data.guide(visitor);
setIntersectingMasked(spanner->getBounds(), data);
}
SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) :
minimum(minimum),
data(data),
blend(blend) {
}
void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.set(minimum, this->data, blend);
}
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) :
position(position),
radius(radius),
height(height) {
}
class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldHeightEdit _edit;
Box _bounds;
};
PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// raise/lower all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
float squaredRadiusReciprocal = 1.0f / squaredRadius;
const int EIGHT_BIT_MAXIMUM = 255;
float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
float distanceSquared = dx * dx + dz * dz;
if (distanceSquared <= squaredRadius) {
// height falls off towards edges
int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
*dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
}
}
lineDest += size;
}
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
return STOP_RECURSION;
}
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldHeightEditVisitor visitor(*this);
data.guide(visitor);
}
PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position, float radius, const QColor& color) :
position(position),
radius(radius),
color(color) {
}
class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldColorEdit _edit;
Box _bounds;
};
PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QByteArray contents(pointer->getContents());
const int BYTES_PER_PIXEL = 3;
int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL);
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
int stride = size * BYTES_PER_PIXEL;
char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL;
float squaredRadius = scaledRadius * scaledRadius;
char red = _edit.color.red(), green = _edit.color.green(), blue = _edit.color.blue();
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
char* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest += BYTES_PER_PIXEL) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
}
}
lineDest += stride;
}
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
return STOP_RECURSION;
}
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldColorEditVisitor visitor(*this);
data.guide(visitor);
}