From 540d07427038c47c7a85cad08e639e130d704edc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 19 Aug 2014 17:20:14 -0700 Subject: [PATCH] More texture bits. --- .../metavoxels/src/AttributeRegistry.cpp | 16 ++-- libraries/metavoxels/src/AttributeRegistry.h | 7 +- .../metavoxels/src/MetavoxelMessages.cpp | 74 +++++++++++++++++-- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 0e56112957..c09848c201 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -932,8 +932,9 @@ void HeightfieldColorData::set(const QImage& image) { memcpy(_contents.data(), image.constBits(), _contents.size()); } -HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents) : - HeightfieldData(contents) { +HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector& textures) : + HeightfieldData(contents), + _textures(textures) { } HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes) { @@ -947,6 +948,7 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H } QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); + in.readDelta(_textures, reference->getTextures()); reference->setDeltaData(HeightfieldDataPointer(this)); _contents = reference->getContents(); QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); @@ -978,6 +980,7 @@ void HeightfieldTextureData::write(Bitstream& out) { } out << _encoded.size(); out.writeAligned(_encoded); + out << _textures; } void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference) { @@ -1023,10 +1026,12 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); + out.writeDelta(_textures, reference->getTextures()); } void HeightfieldTextureData::read(Bitstream& in, int bytes) { set(decodeHeightfieldImage(_encoded = in.readAligned(bytes))); + in >> _textures; } void HeightfieldTextureData::set(const QImage& image) { @@ -1363,11 +1368,8 @@ bool HeightfieldTextureAttribute::merge(void*& parent, void* children[], bool po maxSize = qMax(maxSize, pointer->getContents().size()); } } - if (maxSize == 0) { - *(HeightfieldTextureDataPointer*)&parent = HeightfieldTextureDataPointer(); - return true; - } - return false; + *(HeightfieldTextureDataPointer*)&parent = HeightfieldTextureDataPointer(); + return maxSize == 0; } SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index cceda9aa5d..f3e6dc0b97 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -527,10 +527,13 @@ typedef QExplicitlySharedDataPointer HeightfieldTextureD class HeightfieldTextureData : public HeightfieldData { public: - HeightfieldTextureData(const QByteArray& contents); + HeightfieldTextureData(const QByteArray& contents, + const QVector& textures = QVector()); HeightfieldTextureData(Bitstream& in, int bytes); HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference); + const QVector& getTextures() const { return _textures; } + void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference); @@ -538,6 +541,8 @@ private: void read(Bitstream& in, int bytes); void set(const QImage& image); + + QVector _textures; }; /// Contains the description of a heightfield texture. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 2af19023f1..33aea9fcb1 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -347,6 +347,8 @@ PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const Paint _bounds = Box(_edit.position - extents, _edit.position + extents); } +const int EIGHT_BIT_MAXIMUM = 255; + int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; @@ -375,8 +377,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { 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 squaredRadiusReciprocal = 1.0f / squaredRadius; float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size; bool changed = false; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { @@ -528,6 +529,16 @@ PaintHeightfieldTextureEditVisitor::PaintHeightfieldTextureEditVisitor(const Pai _bounds = Box(_edit.position - extents, _edit.position + extents); } +static QHash countIndices(const QByteArray& contents) { + QHash counts; + for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { + if (*src != 0) { + counts[*src]++; + } + } + return counts; +} + int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; @@ -539,7 +550,47 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { if (!pointer) { return STOP_RECURSION; } + QVector textures = pointer->getTextures(); QByteArray contents(pointer->getContents()); + uchar textureIndex = 0; + if (_edit.texture && static_cast(_edit.texture.data())->getURL().isValid()) { + // first look for a matching existing texture, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < textures.size(); i++) { + const SharedObjectPointer& texture = textures.at(i); + if (texture) { + if (texture->equals(_edit.texture.data())) { + textureIndex = i + 1; + break; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (textureIndex == 0) { + if (firstEmptyIndex != -1) { + textures[firstEmptyIndex] = _edit.texture; + textureIndex = firstEmptyIndex + 1; + + } else if (textures.size() < EIGHT_BIT_MAXIMUM) { + textures.append(_edit.texture); + textureIndex = textures.size(); + + } else { + // last resort: find the least-used texture and remove it + QHash counts = countIndices(contents); + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + textureIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)textureIndex, (char)0); + } + } + } int size = glm::sqrt((float)contents.size()); int highest = size - 1; float heightScale = size / info.size; @@ -554,22 +605,33 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { // 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); - char* lineDest = contents.data() + (int)z * size + (int)startX; + uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; float squaredRadius = scaledRadius * scaledRadius; bool changed = false; + QHash counts; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - char* dest = lineDest; + 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 = 1; + *dest = textureIndex; changed = true; } } lineDest += size; } if (changed) { - HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents)); + // clear any unused textures + QHash counts = countIndices(contents); + for (int i = 0; i < textures.size(); i++) { + if (counts.value(i + 1) == 0) { + textures[i] = SharedObjectPointer(); + } + } + while (!(textures.isEmpty() || textures.last())) { + textures.removeLast(); + } + HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents, textures)); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor);