More voxel editing bits.

This commit is contained in:
Andrzej Kapolka 2014-08-27 18:15:29 -07:00
parent 9a4d56b4e9
commit 96b4a1080c
5 changed files with 510 additions and 246 deletions

View file

@ -978,14 +978,51 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const
glEnable(GL_BLEND);
}
VoxelBuffer::VoxelBuffer(const QVector<VoxelVertex>& vertices, const QVector<int>& indices,
VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices,
const QVector<SharedObjectPointer>& materials) :
_vertices(vertices),
_indices(indices),
_vertexCount(vertices.size()),
_indexCount(indices.size()),
_indexBuffer(QOpenGLBuffer::IndexBuffer),
_materials(materials) {
}
void VoxelBuffer::render(bool cursor) {
if (!_vertexBuffer.isCreated()) {
_vertexBuffer.create();
_vertexBuffer.bind();
_vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint));
_vertices.clear();
_indexBuffer.create();
_indexBuffer.bind();
_indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int));
_indices.clear();
if (!_materials.isEmpty()) {
_networkTextures.resize(_materials.size());
for (int i = 0; i < _materials.size(); i++) {
const SharedObjectPointer material = _materials.at(i);
if (material) {
_networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture(
static_cast<MaterialObject*>(material.data())->getDiffuse(), SPLAT_TEXTURE);
}
}
}
} else {
_vertexBuffer.bind();
_indexBuffer.bind();
}
VoxelPoint* point = 0;
glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex);
glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color);
glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal);
glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0);
_vertexBuffer.release();
_indexBuffer.release();
}
BufferDataAttribute::BufferDataAttribute(const QString& name) :
@ -1506,8 +1543,9 @@ public:
VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) {
AttributeRegistry::getInstance()->getVoxelMaterialAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute(), QVector<AttributePointer>() <<
Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) {
}
int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
@ -1517,7 +1555,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
VoxelBuffer* buffer = NULL;
VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
if (color) {
QVector<VoxelVertex> vertices;
QVector<VoxelPoint> vertices;
QVector<int> indices;
const QVector<QRgb>& contents = color->getContents();
@ -1534,7 +1572,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
const int ALPHA_OFFSET = 24;
const int MAX_ALPHA_TOTAL = EIGHT_BIT_MAXIMUM * 8;
for (int z = 0; z < highest; z++) {
for (int y = 0; y < highest; z++) {
for (int y = 0; y < highest; y++) {
for (int x = 0; x < highest; x++, src++) {
int alpha0 = src[0] >> ALPHA_OFFSET;
int alpha1 = src[1] >> ALPHA_OFFSET;

View file

@ -215,9 +215,9 @@ private:
};
/// Describes contents of a vertex in a voxel buffer.
class VoxelVertex {
class VoxelPoint {
public:
glm::vec3 position;
glm::vec3 vertex;
quint8 color[3];
quint8 normal[3];
};
@ -226,15 +226,19 @@ public:
class VoxelBuffer : public BufferData {
public:
VoxelBuffer(const QVector<VoxelVertex>& vertices, const QVector<int>& indices,
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
virtual void render(bool cursor = false);
private:
QVector<VoxelVertex> _vertices;
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
int _vertexCount;
int _indexCount;
QOpenGLBuffer _vertexBuffer;
QOpenGLBuffer _indexBuffer;
QVector<SharedObjectPointer> _materials;
QVector<NetworkTexturePointer> _networkTextures;
};

View file

@ -31,6 +31,7 @@ REGISTER_META_OBJECT(SharedObjectAttribute)
REGISTER_META_OBJECT(SharedObjectSetAttribute)
REGISTER_META_OBJECT(SpannerSetAttribute)
REGISTER_META_OBJECT(VoxelColorAttribute)
REGISTER_META_OBJECT(VoxelHermiteAttribute)
REGISTER_META_OBJECT(VoxelMaterialAttribute)
static int attributePointerMetaTypeId = qRegisterMetaType<AttributePointer>();
@ -56,7 +57,8 @@ AttributeRegistry::AttributeRegistry() :
_heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))),
_heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))),
_voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))),
_voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))) {
_voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))),
_voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) {
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
@ -72,6 +74,7 @@ AttributeRegistry::AttributeRegistry() :
_voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
_voxelColorAttribute->setUserFacing(true);
_voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
_voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
}
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
@ -1829,6 +1832,194 @@ bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRea
return maxSize == 0;
}
VoxelHermiteData::VoxelHermiteData(const QVector<QRgb>& contents, int size) :
_contents(contents),
_size(size) {
}
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const QRgb* src = delta.constData();
int destStride = _size * EDGE_COUNT;
int destStride2 = _size * destStride;
QRgb* planeDest = _contents.data() + minZ * destStride2 + minY * destStride + minX * EDGE_COUNT;
int srcStride = sizeX * EDGE_COUNT;
int length = srcStride * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeDest += destStride2) {
QRgb* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
memcpy(dest, src, length);
}
}
}
void VoxelHermiteData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
}
void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const QRgb* src = _contents.constData();
const QRgb* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++) {
if (*src++ != *ref++ || *src++ != *ref++ || *src++ != *ref++) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QVector<QRgb> delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QVector<QRgb>(sizeX * sizeY * sizeZ, 0);
QRgb* dest = delta.data();
int srcStride = _size * EDGE_COUNT;
int srcStride2 = _size * srcStride;
const QRgb* planeSrc = _contents.constData() + minZ * srcStride2 + minY * srcStride + minX * EDGE_COUNT;
int destStride = sizeX * EDGE_COUNT;
int length = destStride * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeSrc += srcStride2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
memcpy(dest, src, length);
}
}
}
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
}
void VoxelHermiteData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
}
VoxelHermiteAttribute::VoxelHermiteAttribute(const QString& name) :
InlineAttribute<VoxelHermiteDataPointer>(name) {
}
void VoxelHermiteAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
} else {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(in, size));
}
}
void VoxelHermiteAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelHermiteAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
} else {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(
in, size, decodeInline<VoxelHermiteDataPointer>(reference)));
}
}
void VoxelHermiteAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelHermiteDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelHermiteDataPointer pointer = decodeInline<VoxelHermiteDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer();
return maxSize == 0;
}
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
const SharedObjectPointer& defaultValue) :
InlineAttribute<SharedObjectPointer>(name, defaultValue),

View file

@ -38,6 +38,7 @@ class MetavoxelLOD;
class MetavoxelNode;
class MetavoxelStreamState;
class VoxelColorData;
class VoxelHermiteData;
class VoxelMaterialData;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -117,6 +118,9 @@ public:
/// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute.
const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; }
/// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute.
const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; }
private:
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
@ -137,6 +141,7 @@ private:
AttributePointer _heightfieldMaterialAttribute;
AttributePointer _voxelColorAttribute;
AttributePointer _voxelMaterialAttribute;
AttributePointer _voxelHermiteAttribute;
};
/// Converts a value to a void pointer.
@ -728,6 +733,50 @@ public:
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<VoxelHermiteData> VoxelHermiteDataPointer;
/// Contains a block of voxel Hermite data (positions and normals at edge crossings).
class VoxelHermiteData : public DataBlock {
public:
static const int EDGE_COUNT = 3;
VoxelHermiteData(const QVector<QRgb>& contents, int size);
VoxelHermiteData(Bitstream& in, int bytes);
VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel Hermite data.
class VoxelHermiteAttribute : public InlineAttribute<VoxelHermiteDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelHermiteAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject).
class SharedObjectAttribute : public InlineAttribute<SharedObjectPointer> {
Q_OBJECT

View file

@ -414,119 +414,36 @@ PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position,
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);
}
static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position, float radius, const QColor& color) {
HeightfieldColorDataPointer pointer = info.inputValues.at(index).getInlineValue<HeightfieldColorDataPointer>();
if (!pointer) {
return;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (position - info.minimum) * heightScale;
float scaledRadius = 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 * DataBlock::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = color.red(), green = color.green(), blue = color.blue();
bool changed = false;
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 += DataBlock::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
}
lineDest += stride;
}
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[index] = AttributeValue(info.inputValues.at(index).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
paintColor(info, 0, _edit.position, _edit.radius, _edit.color);
return STOP_RECURSION;
}
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldColorEditVisitor visitor(*this);
data.guide(visitor);
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
position(position),
radius(radius),
material(material),
averageColor(averageColor) {
}
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldMaterialEditVisitor(const PaintHeightfieldMaterialEdit& edit);
PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldMaterialEdit _edit;
glm::vec3 _position;
float _radius;
SharedObjectPointer _material;
QColor _color;
Box _bounds;
};
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const PaintHeightfieldMaterialEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()),
_position(position),
_radius(radius),
_material(material),
_color(color) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
glm::vec3 extents(_radius, _radius, _radius);
_bounds = Box(_position - extents, _position + extents);
}
static QHash<uchar, int> countIndices(const QByteArray& contents) {
@ -546,100 +463,153 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldMaterialDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldMaterialDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QVector<SharedObjectPointer> materials = pointer->getMaterials();
QByteArray contents(pointer->getContents());
uchar materialIndex = 0;
if (_edit.material && static_cast<MaterialObject*>(_edit.material.data())->getDiffuse().isValid()) {
// first look for a matching existing material, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < materials.size(); i++) {
const SharedObjectPointer& material = materials.at(i);
if (material) {
if (material->equals(_edit.material.data())) {
materialIndex = i + 1;
break;
HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
if (colorPointer) {
QByteArray contents(colorPointer->getContents());
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _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 * DataBlock::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = _color.red(), green = _color.green(), blue = _color.blue();
bool changed = false;
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 += DataBlock::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
lineDest += stride;
}
// if nothing found, use the first empty slot or append
if (materialIndex == 0) {
if (firstEmptyIndex != -1) {
materials[firstEmptyIndex] = _edit.material;
materialIndex = firstEmptyIndex + 1;
} else if (materials.size() < EIGHT_BIT_MAXIMUM) {
materials.append(_edit.material);
materialIndex = materials.size();
} else {
// last resort: find the least-used material and remove it
QHash<uchar, int> counts = countIndices(contents);
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
materialIndex = it.key();
lowestCount = it.value();
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
QByteArray contents(materialPointer->getContents());
uchar materialIndex = 0;
if (_material && static_cast<MaterialObject*>(_material.data())->getDiffuse().isValid()) {
// first look for a matching existing material, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < materials.size(); i++) {
const SharedObjectPointer& material = materials.at(i);
if (material) {
if (material->equals(_material.data())) {
materialIndex = i + 1;
break;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
}
// if nothing found, use the first empty slot or append
if (materialIndex == 0) {
if (firstEmptyIndex != -1) {
materials[firstEmptyIndex] = _material;
materialIndex = firstEmptyIndex + 1;
} else if (materials.size() < EIGHT_BIT_MAXIMUM) {
materials.append(_material);
materialIndex = materials.size();
} else {
// last resort: find the least-used material and remove it
QHash<uchar, int> counts = countIndices(contents);
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
materialIndex = it.key();
lowestCount = it.value();
}
}
contents.replace((char)materialIndex, (char)0);
}
contents.replace((char)materialIndex, (char)0);
}
}
}
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);
// 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);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
QHash<uchar, int> counts;
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;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = materialIndex;
changed = true;
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _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);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
QHash<uchar, int> counts;
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;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = materialIndex;
changed = true;
}
}
lineDest += size;
}
lineDest += size;
}
if (changed) {
// clear any unused materials
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < materials.size(); i++) {
if (counts.value(i + 1) == 0) {
materials[i] = SharedObjectPointer();
if (changed) {
// clear any unused materials
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < materials.size(); i++) {
if (counts.value(i + 1) == 0) {
materials[i] = SharedObjectPointer();
}
}
while (!(materials.isEmpty() || materials.last())) {
materials.removeLast();
}
HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials));
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldMaterialDataPointer>(newPointer));
}
while (!(materials.isEmpty() || materials.last())) {
materials.removeLast();
}
HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldMaterialDataPointer>(newPointer));
}
paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor);
return STOP_RECURSION;
}
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(position, radius, SharedObjectPointer(), color);
data.guide(visitor);
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
position(position),
radius(radius),
material(material),
averageColor(averageColor) {
}
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(*this);
PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor);
data.guide(visitor);
}
@ -654,59 +624,98 @@ const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1;
const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES;
const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES;
class VoxelColorBoxEditVisitor : public MetavoxelVisitor {
class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor {
public:
VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit);
VoxelMaterialBoxEditVisitor(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
const VoxelColorBoxEdit& _edit;
Box _region;
float _granularity;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelColorBoxEditVisitor::VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute()),
_edit(edit),
_blockSize(edit.granularity * VOXEL_BLOCK_SIZE) {
VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_region(region),
_granularity(granularity),
_material(material),
_color(color),
_blockSize(granularity * VOXEL_BLOCK_SIZE) {
}
int VoxelColorBoxEditVisitor::visit(MetavoxelInfo& info) {
int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_edit.region)) {
if (!bounds.intersects(_region)) {
return STOP_RECURSION;
}
if (!info.size > _blockSize) {
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer pointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> contents = (pointer && pointer->getSize() == VOXEL_BLOCK_SAMPLES) ? pointer->getContents() :
QVector<QRgb>(VOXEL_BLOCK_VOLUME);
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
Box overlap = bounds.getIntersection(_edit.region);
int minX = (overlap.minimum.x - info.minimum.x) / _edit.granularity;
int minY = (overlap.minimum.y - info.minimum.y) / _edit.granularity;
int minZ = (overlap.minimum.z - info.minimum.z) / _edit.granularity;
int sizeX = (overlap.maximum.x - overlap.minimum.x) / _edit.granularity;
int sizeY = (overlap.maximum.y - overlap.minimum.y) / _edit.granularity;
int sizeZ = (overlap.maximum.z - overlap.minimum.z) / _edit.granularity;
Box overlap = info.getBounds().getIntersection(_region);
float scale = VOXEL_BLOCK_SIZE / info.size;
int minX = glm::ceil((overlap.minimum.x - info.minimum.x) * scale);
int minY = glm::ceil((overlap.minimum.y - info.minimum.y) * scale);
int minZ = glm::ceil((overlap.minimum.z - info.minimum.z) * scale);
int sizeX = (int)((overlap.maximum.x - info.minimum.x) * scale) - minX + 1;
int sizeY = (int)((overlap.maximum.y - info.minimum.y) * scale) - minY + 1;
int sizeZ = (int)((overlap.maximum.z - info.minimum.z) * scale) - minZ + 1;
QRgb color = _edit.color.rgb();
for (QRgb* destZ = contents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
QRgb rgb = _color.rgb();
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) {
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) {
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++) {
*destX = color;
*destX = rgb;
}
}
}
VoxelColorDataPointer newPointer(new VoxelColorData(contents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<VoxelColorDataPointer>(newPointer));
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
QVector<QRgb> hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
hermitePointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
QByteArray materialContents = (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
materialPointer->getContents() : QByteArray(VOXEL_BLOCK_VOLUME, 0);
char material = 0;
for (char* destZ = materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) {
for (char* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) {
for (char* destX = destY, *endX = destX + sizeX; destX != endX; destX++) {
*destX = material;
}
}
}
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
return STOP_RECURSION;
}
@ -715,8 +724,8 @@ void VoxelColorBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o
while (!data.getBounds().contains(region)) {
data.expand();
}
VoxelColorBoxEditVisitor setVisitor(*this);
data.guide(setVisitor);
VoxelMaterialBoxEditVisitor visitor(region, granularity, SharedObjectPointer(), color);
data.guide(visitor);
}
VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity,
@ -727,38 +736,11 @@ VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity,
averageColor(averageColor) {
}
class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialBoxEditVisitor(const VoxelMaterialBoxEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
const VoxelMaterialBoxEdit& _edit;
};
VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const VoxelMaterialBoxEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelMaterialAttribute() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute()),
_edit(edit) {
}
int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
return STOP_RECURSION;
}
void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
}
VoxelMaterialBoxEditVisitor setVisitor(*this);
data.guide(setVisitor);
VoxelMaterialBoxEditVisitor visitor(region, granularity, material, averageColor);
data.guide(visitor);
}