From 8804fa37109ff82a8f15cc847d2091780f442815 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 11 Aug 2014 14:29:53 -0700 Subject: [PATCH 01/10] Switch to PNG encoding, send only the changed portion of the image for deltas. --- .../metavoxels/src/AttributeRegistry.cpp | 275 +++++++++++++++--- libraries/metavoxels/src/AttributeRegistry.h | 24 +- .../metavoxels/src/MetavoxelMessages.cpp | 22 +- 3 files changed, 268 insertions(+), 53 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9f656ef5d3..2943ac152b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -490,21 +490,52 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) : } const int BYTES_PER_PIXEL = 3; +const int ZERO_OFFSET = 128; -HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) : - _encoded(in.readAligned(bytes)) { +HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { + read(in, bytes, color); +} + +HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color) { + if (!reference) { + read(in, bytes, color); + return; + } + QMutexLocker locker(&reference->_encodedDeltaMutex); + reference->_encodedDelta = in.readAligned(bytes); + reference->_deltaData = this; + _contents = reference->_contents; - QImage image = QImage::fromData(_encoded).convertToFormat(QImage::Format_RGB888); + QBuffer buffer(&reference->_encodedDelta); + buffer.open(QIODevice::ReadOnly); + QImage image; + image.load(&buffer, "PNG"); + QPoint offset = image.offset(); + image = image.convertToFormat(QImage::Format_RGB888); + if (offset.x() == 0) { + set(image, color); + return; + } + int minX = offset.x() - 1; + int minY = offset.y() - 1; if (color) { - _contents.resize(image.width() * image.height() * BYTES_PER_PIXEL); - memcpy(_contents.data(), image.constBits(), _contents.size()); - + int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL); + char* dest = _contents.data() + (minY * size + minX) * BYTES_PER_PIXEL; + int destStride = size * BYTES_PER_PIXEL; + int srcStride = image.width() * BYTES_PER_PIXEL; + for (int y = 0; y < image.height(); y++) { + memcpy(dest, image.constScanLine(y), srcStride); + dest += destStride; + } } else { - _contents.resize(image.width() * image.height()); - char* dest = _contents.data(); - for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL; - src != end; src += BYTES_PER_PIXEL) { - *dest++ = *src; + int size = glm::sqrt((float)_contents.size()); + char* lineDest = _contents.data() + minY * size + minX; + for (int y = 0; y < image.height(); y++) { + const uchar* src = image.constScanLine(y); + for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += BYTES_PER_PIXEL) { + *dest = *src; + } + lineDest += size; } } } @@ -528,36 +559,168 @@ void HeightfieldData::write(Bitstream& out, bool color) { } QBuffer buffer(&_encoded); buffer.open(QIODevice::WriteOnly); - image.save(&buffer, "JPG"); + image.save(&buffer, "PNG"); } out << _encoded.size(); out.writeAligned(_encoded); } +void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color) { + if (!reference || reference->getContents().size() != _contents.size()) { + write(out, color); + return; + } + QMutexLocker locker(&reference->_encodedDeltaMutex); + if (reference->_encodedDelta.isEmpty() || reference->_deltaData != this) { + QImage image; + int minX, minY; + if (color) { + int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL); + minX = size; + minY = size; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->_contents.constData(); + for (int y = 0; y < size; y++) { + bool difference = false; + for (int x = 0; x < size; x++, src += BYTES_PER_PIXEL, ref += BYTES_PER_PIXEL) { + if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + int width = qMax(maxX - minX + 1, 0); + int height = qMax(maxY - minY + 1, 0); + image = QImage(width, height, QImage::Format_RGB888); + src = _contents.constData() + (minY * size + minX) * BYTES_PER_PIXEL; + int srcStride = size * BYTES_PER_PIXEL; + int destStride = width * BYTES_PER_PIXEL; + for (int y = 0; y < height; y++) { + memcpy(image.scanLine(y), src, destStride); + src += srcStride; + } + } else { + int size = glm::sqrt((float)_contents.size()); + minX = size; + minY = size; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->_contents.constData(); + for (int y = 0; y < size; y++) { + bool difference = false; + for (int x = 0; x < size; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + int width = qMax(maxX - minX + 1, 0); + int height = qMax(maxY - minY + 1, 0); + image = QImage(width, height, QImage::Format_RGB888); + const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX; + for (int y = 0; y < height; y++) { + uchar* dest = image.scanLine(y); + for (const uchar* src = lineSrc, *end = src + width; src != end; src++) { + *dest++ = *src; + *dest++ = *src; + *dest++ = *src; + } + lineSrc += size; + } + } + QBuffer buffer(&reference->_encodedDelta); + buffer.open(QIODevice::WriteOnly); + image.setOffset(QPoint(minX + 1, minY + 1)); + image.save(&buffer, "PNG"); + reference->_deltaData = this; + } + out << reference->_encodedDelta.size(); + out.writeAligned(reference->_encodedDelta); +} + +void HeightfieldData::read(Bitstream& in, int bytes, bool color) { + set(QImage::fromData(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color); +} + +void HeightfieldData::set(const QImage& image, bool color) { + if (color) { + _contents.resize(image.width() * image.height() * BYTES_PER_PIXEL); + memcpy(_contents.data(), image.constBits(), _contents.size()); + + } else { + _contents.resize(image.width() * image.height()); + char* dest = _contents.data(); + for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL; + src != end; src += BYTES_PER_PIXEL) { + *dest++ = *src; + } + } +} + HeightfieldAttribute::HeightfieldAttribute(const QString& name) : InlineAttribute(name) { } void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (isLeaf) { - int size; - in >> size; - if (size == 0) { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); - } else { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false)); - } + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + } else { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false)); } } void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (isLeaf) { - HeightfieldDataPointer data = decodeInline(value); - if (data) { - data->write(out, false); - } else { - out << 0; - } + if (!isLeaf) { + return; + } + HeightfieldDataPointer data = decodeInline(value); + if (data) { + data->write(out, false); + } else { + out << 0; + } +} + +void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + } else { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData( + in, size, decodeInline(reference), false)); + } +} + +void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + HeightfieldDataPointer data = decodeInline(value); + if (data) { + data->writeDelta(out, decodeInline(reference), false); + } else { + out << 0; } } @@ -635,25 +798,53 @@ HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : } void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (isLeaf) { - int size; - in >> size; - if (size == 0) { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); - } else { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true)); - } + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + } else { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true)); } } void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (isLeaf) { - HeightfieldDataPointer data = decodeInline(value); - if (data) { - data->write(out, true); - } else { - out << 0; - } + if (!isLeaf) { + return; + } + HeightfieldDataPointer data = decodeInline(value); + if (data) { + data->write(out, true); + } else { + out << 0; + } +} + +void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + } else { + *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData( + in, size, decodeInline(reference), true)); + } +} + +void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + HeightfieldDataPointer data = decodeInline(value); + if (data) { + data->writeDelta(out, decodeInline(reference), true); + } else { + out << 0; } } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 5d973341ad..d0f1cdf836 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -28,6 +28,7 @@ class QScriptEngine; class QScriptValue; class Attribute; +class HeightfieldData; class MetavoxelData; class MetavoxelLOD; class MetavoxelNode; @@ -421,26 +422,35 @@ public: virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; +typedef QExplicitlySharedDataPointer HeightfieldDataPointer; + /// Contains a block of heightfield data. class HeightfieldData : public QSharedData { public: HeightfieldData(const QByteArray& contents); HeightfieldData(Bitstream& in, int bytes, bool color); - + HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color); + const QByteArray& getContents() const { return _contents; } void write(Bitstream& out, bool color); + void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color); private: + void read(Bitstream& in, int bytes, bool color); + void set(const QImage& image, bool color); + QByteArray _contents; QByteArray _encoded; QMutex _encodedMutex; + + HeightfieldDataPointer _deltaData; + QByteArray _encodedDelta; + QMutex _encodedDeltaMutex; }; -typedef QExplicitlySharedDataPointer HeightfieldDataPointer; - /// An attribute that stores heightfield data. class HeightfieldAttribute : public InlineAttribute { Q_OBJECT @@ -451,7 +461,10 @@ public: 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; }; @@ -466,6 +479,9 @@ public: 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; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 926d839991..ad3c36eb03 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -378,6 +378,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { float squaredRadiusReciprocal = 1.0f / squaredRadius; const int EIGHT_BIT_MAXIMUM = 255; 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) { uchar* dest = lineDest; for (float x = startX; x <= endX; x += 1.0f, dest++) { @@ -386,14 +387,18 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { if (distanceSquared <= squaredRadius) { // height falls off towards edges int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - *dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM); + if (value != *dest) { + *dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM); + changed = true; + } } } lineDest += size; } - - HeightfieldDataPointer newPointer(new HeightfieldData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + if (changed) { + HeightfieldDataPointer newPointer(new HeightfieldData(contents)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + } return STOP_RECURSION; } @@ -461,6 +466,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { 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(); + 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 += BYTES_PER_PIXEL) { @@ -469,13 +475,15 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { dest[0] = red; dest[1] = green; dest[2] = blue; + changed = true; } } lineDest += stride; } - - HeightfieldDataPointer newPointer(new HeightfieldData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + if (changed) { + HeightfieldDataPointer newPointer(new HeightfieldData(contents)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + } return STOP_RECURSION; } From 10315f15d452adf0051679692cfcffe395588798 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 11 Aug 2014 15:15:33 -0700 Subject: [PATCH 02/10] Simplify image loading. --- libraries/metavoxels/src/AttributeRegistry.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 2943ac152b..7e70d7b315 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -505,11 +505,7 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData reference->_encodedDelta = in.readAligned(bytes); reference->_deltaData = this; _contents = reference->_contents; - - QBuffer buffer(&reference->_encodedDelta); - buffer.open(QIODevice::ReadOnly); - QImage image; - image.load(&buffer, "PNG"); + QImage image = QImage::fromData(reference->_encodedDelta); QPoint offset = image.offset(); image = image.convertToFormat(QImage::Format_RGB888); if (offset.x() == 0) { From 2c0f8ff57ec0b70fbe72ba97ddd12473c5ac90e8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 12 Aug 2014 10:56:15 -0700 Subject: [PATCH 03/10] Working on rejiggering the heightfield tiles. --- .../shaders/metavoxel_heightfield.vert | 7 +- .../shaders/metavoxel_heightfield_cursor.vert | 3 - interface/src/MetavoxelSystem.cpp | 100 +++++++++++++----- interface/src/MetavoxelSystem.h | 6 +- interface/src/ui/MetavoxelEditor.cpp | 30 +++--- 5 files changed, 100 insertions(+), 46 deletions(-) diff --git a/interface/resources/shaders/metavoxel_heightfield.vert b/interface/resources/shaders/metavoxel_heightfield.vert index cc4f68e9e0..4932ad1b2b 100644 --- a/interface/resources/shaders/metavoxel_heightfield.vert +++ b/interface/resources/shaders/metavoxel_heightfield.vert @@ -17,6 +17,9 @@ uniform sampler2D heightMap; // the distance between height points in texture space uniform float heightScale; +// the scale between height and color textures +uniform float colorScale; + // the interpolated normal varying vec4 normal; @@ -29,8 +32,8 @@ void main(void) { texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r; normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0)); - // pass along the texture coordinates - gl_TexCoord[0] = gl_MultiTexCoord0; + // pass along the scaled/offset texture coordinates + gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale; // add the height to the position float height = texture2D(heightMap, heightCoord).r; diff --git a/interface/resources/shaders/metavoxel_heightfield_cursor.vert b/interface/resources/shaders/metavoxel_heightfield_cursor.vert index 20502fbdce..93ed36449e 100644 --- a/interface/resources/shaders/metavoxel_heightfield_cursor.vert +++ b/interface/resources/shaders/metavoxel_heightfield_cursor.vert @@ -14,9 +14,6 @@ // the height texture uniform sampler2D heightMap; -// the distance between height points in texture space -uniform float heightScale; - void main(void) { // compute the view space coordinates float height = texture2D(heightMap, gl_MultiTexCoord0.st).r; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 597542778a..7be3955de7 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -366,6 +366,7 @@ void HeightfieldBuffer::render(bool cursor) { if (_heightTextureID == 0) { glGenTextures(1, &_heightTextureID); glBindTexture(GL_TEXTURE_2D, _heightTextureID); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -385,7 +386,7 @@ void HeightfieldBuffer::render(bool cursor) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); } else { - int colorSize = glm::sqrt(_color.size() / 3); + int colorSize = glm::sqrt(_color.size() / 3); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); if (_clearAfterLoading) { _color.clear(); @@ -393,24 +394,27 @@ void HeightfieldBuffer::render(bool cursor) { } } // create the buffer objects lazily - int sizeWithSkirt = _heightSize + 2; - int vertexCount = sizeWithSkirt * sizeWithSkirt; - int rows = sizeWithSkirt - 1; + int innerSize = _heightSize - 2; + int vertexCount = _heightSize * _heightSize; + int rows = _heightSize - 1; int indexCount = rows * rows * 4; BufferPair& bufferPair = _bufferPairs[_heightSize]; if (!bufferPair.first.isCreated()) { QVector vertices(vertexCount); HeightfieldPoint* point = vertices.data(); - float step = 1.0f / (_heightSize - 1); - float z = -step; - for (int i = 0; i < sizeWithSkirt; i++, z += step) { - float x = -step; + float vertexStep = 1.0f / (innerSize - 1); + float z = -vertexStep; + float textureStep = 1.0f / _heightSize; + float t = textureStep / 2.0f; + for (int i = 0; i < _heightSize; i++, z += vertexStep, t += textureStep) { + float x = -vertexStep; + float s = textureStep / 2.0f; const float SKIRT_LENGTH = 0.25f; - float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f; - for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) { - point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z); - point->textureCoord = glm::vec2(x, z); + float baseY = (i == 0 || i == _heightSize - 1) ? -SKIRT_LENGTH : 0.0f; + for (int j = 0; j < _heightSize; j++, point++, x += vertexStep, s += textureStep) { + point->vertex = glm::vec3(x, (j == 0 || j == _heightSize - 1) ? -SKIRT_LENGTH : baseY, z); + point->textureCoord = glm::vec2(s, t); } } @@ -422,8 +426,8 @@ void HeightfieldBuffer::render(bool cursor) { QVector indices(indexCount); int* index = indices.data(); for (int i = 0; i < rows; i++) { - int lineIndex = i * sizeWithSkirt; - int nextLineIndex = (i + 1) * sizeWithSkirt; + int lineIndex = i * _heightSize; + int nextLineIndex = (i + 1) * _heightSize; for (int j = 0; j < rows; j++) { *index++ = lineIndex + j; *index++ = nextLineIndex + j; @@ -452,17 +456,15 @@ void HeightfieldBuffer::render(bool cursor) { glBindTexture(GL_TEXTURE_2D, _heightTextureID); - int heightScaleLocation; - if (cursor) { - heightScaleLocation = DefaultMetavoxelRendererImplementation::getCursorHeightScaleLocation(); - } else { - heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation(); + if (!cursor) { + DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize); + DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getColorScaleLocation(), (float)_heightSize / innerSize); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _colorTextureID); } - DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(heightScaleLocation, 1.0f / _heightSize); - glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); if (!cursor) { @@ -548,6 +550,7 @@ void DefaultMetavoxelRendererImplementation::init() { _heightfieldProgram.setUniformValue("heightMap", 0); _heightfieldProgram.setUniformValue("diffuseMap", 1); _heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale"); + _colorScaleLocation = _heightfieldProgram.uniformLocation("colorScale"); _heightfieldProgram.release(); _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + @@ -558,7 +561,6 @@ void DefaultMetavoxelRendererImplementation::init() { _heightfieldCursorProgram.bind(); _heightfieldCursorProgram.setUniformValue("heightMap", 0); - _cursorHeightScaleLocation = _heightfieldCursorProgram.uniformLocation("heightScale"); _heightfieldCursorProgram.release(); } } @@ -648,14 +650,62 @@ HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) : Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) { } +class BorderFetchVisitor : public MetavoxelVisitor { +public: + + BorderFetchVisitor(const MetavoxelLOD& lod, QByteArray& height); + + virtual int visit(MetavoxelInfo& info); + +private: + + QByteArray& _height; +}; + +BorderFetchVisitor::BorderFetchVisitor(const MetavoxelLOD& lod, QByteArray& height) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), + QVector(), lod), + _height(height) { +} + +int BorderFetchVisitor::visit(MetavoxelInfo& info) { + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + return STOP_RECURSION; +} + int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { if (info.isLeaf) { HeightfieldBuffer* buffer = NULL; HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); if (height) { + const QByteArray& heightContents = height->getContents(); + int size = glm::sqrt(heightContents.size()); + int extendedSize = size + 3; + QByteArray extendedHeightContents(extendedSize * extendedSize, 0); + char* dest = extendedHeightContents.data() + extendedSize + 1; + const char* src = heightContents.constData(); + for (int z = 0; z < size; z++, src += size, dest += extendedSize) { + memcpy(dest, src, size); + } + QByteArray extendedColorContents; HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); - buffer = new HeightfieldBuffer(info.minimum, info.size, height->getContents(), - color ? color->getContents() : QByteArray()); + if (color) { + const QByteArray& colorContents = color->getContents(); + const int BYTES_PER_PIXEL = 3; + int colorSize = glm::sqrt(colorContents.size() / BYTES_PER_PIXEL); + int extendedColorSize = colorSize + 1; + extendedColorContents = QByteArray(extendedColorSize * extendedColorSize * BYTES_PER_PIXEL, 0); + char* dest = extendedColorContents.data(); + const char* src = colorContents.constData(); + int srcStride = colorSize * BYTES_PER_PIXEL; + int destStride = extendedColorSize * BYTES_PER_PIXEL; + for (int z = 0; z < colorSize; z++, src += srcStride, dest += destStride) { + memcpy(dest, src, srcStride); + } + } + buffer = new HeightfieldBuffer(info.minimum, info.size, extendedHeightContents, extendedColorContents); } info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); return STOP_RECURSION; @@ -856,8 +906,8 @@ ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; int DefaultMetavoxelRendererImplementation::_pointScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram; int DefaultMetavoxelRendererImplementation::_heightScaleLocation; +int DefaultMetavoxelRendererImplementation::_colorScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; -int DefaultMetavoxelRendererImplementation::_cursorHeightScaleLocation; static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { GLdouble coefficients[] = { x, y, z, w }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7b681ff3a9..0940ea4d19 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -194,9 +194,9 @@ public: static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; } static int getHeightScaleLocation() { return _heightScaleLocation; } - + static int getColorScaleLocation() { return _colorScaleLocation; } + static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; } - static int getCursorHeightScaleLocation() { return _cursorHeightScaleLocation; } Q_INVOKABLE DefaultMetavoxelRendererImplementation(); @@ -211,9 +211,9 @@ private: static ProgramObject _heightfieldProgram; static int _heightScaleLocation; + static int _colorScaleLocation; static ProgramObject _heightfieldCursorProgram; - static int _cursorHeightScaleLocation; }; /// Base class for spanner renderers; provides clipping. diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index d35ed93f1b..260d3c2b3b 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1003,30 +1003,34 @@ void ImportHeightfieldTool::updatePreview() { if (_heightImage.width() > 0 && _heightImage.height() > 0) { float z = 0.0f; int blockSize = pow(2.0, _blockSize->value()); - int blockAdvancement = blockSize - 1; - for (int i = 0; i < _heightImage.height(); i += blockAdvancement, z++) { + int heightSize = blockSize + 3; + int colorSize = blockSize + 1; + for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { float x = 0.0f; - for (int j = 0; j < _heightImage.width(); j += blockAdvancement, x++) { - QByteArray height(blockSize * blockSize, 0); - int rows = qMin(blockSize, _heightImage.height() - i); - int columns = qMin(blockSize, _heightImage.width() - j); + for (int j = 0; j < _heightImage.width(); j += blockSize, x++) { + QByteArray height(heightSize * heightSize, 0); + int extendedI = qMax(i - 1, 0); + int extendedJ = qMax(j - 1, 0); + int offsetY = extendedI - i + 1; + int offsetX = extendedJ - j + 1; + int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI); + int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ); const int BYTES_PER_COLOR = 3; for (int y = 0; y < rows; y++) { - uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR; - char* dest = height.data() + y * blockSize; + uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * BYTES_PER_COLOR; + char* dest = height.data() + (y + offsetY) * heightSize + offsetX; for (int x = 0; x < columns; x++) { *dest++ = *src; src += BYTES_PER_COLOR; } } - QByteArray color; if (!_colorImage.isNull()) { - color = QByteArray(blockSize * blockSize * BYTES_PER_COLOR, 0); - rows = qMax(0, qMin(blockSize, _colorImage.height() - i)); - columns = qMax(0, qMin(blockSize, _colorImage.width() - j)); + color = QByteArray(colorSize * colorSize * BYTES_PER_COLOR, 0); + rows = qMax(0, qMin(colorSize, _colorImage.height() - i)); + columns = qMax(0, qMin(colorSize, _colorImage.width() - j)); for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * blockSize * BYTES_PER_COLOR, + memcpy(color.data() + y * colorSize * BYTES_PER_COLOR, _colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR); } } From ee01d85b5ed3769cd8bcc64b7105c0bc59c45274 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 12 Aug 2014 12:01:07 -0700 Subject: [PATCH 04/10] Attempting to remove some number magic. --- interface/src/MetavoxelSystem.cpp | 54 ++++++++++----- interface/src/MetavoxelSystem.h | 13 ++-- interface/src/ui/MetavoxelEditor.cpp | 30 ++++---- .../metavoxels/src/AttributeRegistry.cpp | 69 +++++++++---------- libraries/metavoxels/src/AttributeRegistry.h | 2 + 5 files changed, 95 insertions(+), 73 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7be3955de7..9286b9037b 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -333,12 +333,11 @@ void PointBuffer::render(bool cursor) { } HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, - const QByteArray& height, const QByteArray& color, bool clearAfterLoading) : + const QByteArray& height, const QByteArray& color) : _translation(translation), _scale(scale), _height(height), _color(color), - _clearAfterLoading(clearAfterLoading), _heightTextureID(0), _colorTextureID(0), _heightSize(glm::sqrt(height.size())) { @@ -355,6 +354,32 @@ HeightfieldBuffer::~HeightfieldBuffer() { } } +QByteArray HeightfieldBuffer::getUnextendedHeight() const { + int srcSize = glm::sqrt(_height.size()); + int destSize = srcSize - 3; + QByteArray unextended(destSize * destSize, 0); + const char* src = _height.constData() + srcSize + 1; + char* dest = unextended.data(); + for (int z = 0; z < destSize; z++, src += srcSize, dest += destSize) { + memcpy(dest, src, destSize); + } + return unextended; +} + +QByteArray HeightfieldBuffer::getUnextendedColor() const { + int srcSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES); + int destSize = srcSize - 1; + QByteArray unextended(destSize * destSize * HeightfieldData::COLOR_BYTES, 0); + const char* src = _color.constData(); + int srcStride = srcSize * HeightfieldData::COLOR_BYTES; + char* dest = unextended.data(); + int destStride = destSize * HeightfieldData::COLOR_BYTES; + for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) { + memcpy(dest, src, destStride); + } + return unextended; +} + class HeightfieldPoint { public: glm::vec2 textureCoord; @@ -372,9 +397,6 @@ void HeightfieldBuffer::render(bool cursor) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData()); - if (_clearAfterLoading) { - _height.clear(); - } glGenTextures(1, &_colorTextureID); glBindTexture(GL_TEXTURE_2D, _colorTextureID); @@ -386,15 +408,12 @@ void HeightfieldBuffer::render(bool cursor) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); } else { - int colorSize = glm::sqrt(_color.size() / 3); + int colorSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); - if (_clearAfterLoading) { - _color.clear(); - } } } // create the buffer objects lazily - int innerSize = _heightSize - 2; + int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER; int vertexCount = _heightSize * _heightSize; int rows = _heightSize - 1; int indexCount = rows * rows * 4; @@ -682,9 +701,9 @@ int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { if (height) { const QByteArray& heightContents = height->getContents(); int size = glm::sqrt(heightContents.size()); - int extendedSize = size + 3; + int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION; QByteArray extendedHeightContents(extendedSize * extendedSize, 0); - char* dest = extendedHeightContents.data() + extendedSize + 1; + char* dest = extendedHeightContents.data() + (extendedSize + 1) * HeightfieldBuffer::HEIGHT_BORDER; const char* src = heightContents.constData(); for (int z = 0; z < size; z++, src += size, dest += extendedSize) { memcpy(dest, src, size); @@ -693,14 +712,13 @@ int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); if (color) { const QByteArray& colorContents = color->getContents(); - const int BYTES_PER_PIXEL = 3; - int colorSize = glm::sqrt(colorContents.size() / BYTES_PER_PIXEL); - int extendedColorSize = colorSize + 1; - extendedColorContents = QByteArray(extendedColorSize * extendedColorSize * BYTES_PER_PIXEL, 0); + int colorSize = glm::sqrt(colorContents.size() / HeightfieldData::COLOR_BYTES); + int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE; + extendedColorContents = QByteArray(extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES, 0); char* dest = extendedColorContents.data(); const char* src = colorContents.constData(); - int srcStride = colorSize * BYTES_PER_PIXEL; - int destStride = extendedColorSize * BYTES_PER_PIXEL; + int srcStride = colorSize * HeightfieldData::COLOR_BYTES; + int destStride = extendedColorSize * HeightfieldData::COLOR_BYTES; for (int z = 0; z < colorSize; z++, src += srcStride, dest += destStride) { memcpy(dest, src, srcStride); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 0940ea4d19..a35155dbd6 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -131,10 +131,11 @@ private: class HeightfieldBuffer : public BufferData { public: - /// Creates a new heightfield buffer. - /// \param clearAfterLoading if true, clear the data arrays after we load them into textures in order to reclaim the space - HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color, - bool clearAfterLoading = true); + static const int HEIGHT_BORDER = 1; + static const int SHARED_EDGE = 1; + static const int HEIGHT_EXTENSION = 2 * HEIGHT_BORDER + SHARED_EDGE; + + HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color); ~HeightfieldBuffer(); const glm::vec3& getTranslation() const { return _translation; } @@ -142,6 +143,9 @@ public: const QByteArray& getHeight() const { return _height; } const QByteArray& getColor() const { return _color; } + QByteArray getUnextendedHeight() const; + QByteArray getUnextendedColor() const; + virtual void render(bool cursor = false); private: @@ -150,7 +154,6 @@ private: float _scale; QByteArray _height; QByteArray _color; - bool _clearAfterLoading; GLuint _heightTextureID; GLuint _colorTextureID; int _heightSize; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 260d3c2b3b..b7057532fb 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -956,11 +956,11 @@ void ImportHeightfieldTool::apply() { HeightfieldBuffer* buffer = static_cast(bufferData.data()); MetavoxelData data; data.setSize(scale); - HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight())); + HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getUnextendedHeight())); data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); if (!buffer->getColor().isEmpty()) { - HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor())); + HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor())); data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); } @@ -1003,38 +1003,38 @@ void ImportHeightfieldTool::updatePreview() { if (_heightImage.width() > 0 && _heightImage.height() > 0) { float z = 0.0f; int blockSize = pow(2.0, _blockSize->value()); - int heightSize = blockSize + 3; - int colorSize = blockSize + 1; + int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; + int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE; for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { float x = 0.0f; for (int j = 0; j < _heightImage.width(); j += blockSize, x++) { QByteArray height(heightSize * heightSize, 0); - int extendedI = qMax(i - 1, 0); - int extendedJ = qMax(j - 1, 0); - int offsetY = extendedI - i + 1; - int offsetX = extendedJ - j + 1; + int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0); + int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0); + int offsetY = extendedI - i + HeightfieldBuffer::HEIGHT_BORDER; + int offsetX = extendedJ - j + HeightfieldBuffer::HEIGHT_BORDER; int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI); int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ); - const int BYTES_PER_COLOR = 3; for (int y = 0; y < rows; y++) { - uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * BYTES_PER_COLOR; + uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES; char* dest = height.data() + (y + offsetY) * heightSize + offsetX; for (int x = 0; x < columns; x++) { *dest++ = *src; - src += BYTES_PER_COLOR; + src += HeightfieldData::COLOR_BYTES; } } QByteArray color; if (!_colorImage.isNull()) { - color = QByteArray(colorSize * colorSize * BYTES_PER_COLOR, 0); + color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0); rows = qMax(0, qMin(colorSize, _colorImage.height() - i)); columns = qMax(0, qMin(colorSize, _colorImage.width() - j)); for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * colorSize * BYTES_PER_COLOR, - _colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR); + memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES, + _colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES, + columns * HeightfieldData::COLOR_BYTES); } } - buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color, false))); + buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); } } } diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 7e70d7b315..0f974b82df 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -489,9 +489,6 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) : _contents(contents) { } -const int BYTES_PER_PIXEL = 3; -const int ZERO_OFFSET = 128; - HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { read(in, bytes, color); } @@ -515,10 +512,10 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData int minX = offset.x() - 1; int minY = offset.y() - 1; if (color) { - int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL); - char* dest = _contents.data() + (minY * size + minX) * BYTES_PER_PIXEL; - int destStride = size * BYTES_PER_PIXEL; - int srcStride = image.width() * BYTES_PER_PIXEL; + int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); + char* dest = _contents.data() + (minY * size + minX) * COLOR_BYTES; + int destStride = size * COLOR_BYTES; + int srcStride = image.width() * COLOR_BYTES; for (int y = 0; y < image.height(); y++) { memcpy(dest, image.constScanLine(y), srcStride); dest += destStride; @@ -528,7 +525,7 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData char* lineDest = _contents.data() + minY * size + minX; for (int y = 0; y < image.height(); y++) { const uchar* src = image.constScanLine(y); - for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += BYTES_PER_PIXEL) { + for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += COLOR_BYTES) { *dest = *src; } lineDest += size; @@ -541,7 +538,7 @@ void HeightfieldData::write(Bitstream& out, bool color) { if (_encoded.isEmpty()) { QImage image; if (color) { - int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL); + int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); } else { int size = glm::sqrt((float)_contents.size()); @@ -571,7 +568,7 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r QImage image; int minX, minY; if (color) { - int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL); + int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); minX = size; minY = size; int maxX = -1, maxY = -1; @@ -579,7 +576,7 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r const char* ref = reference->_contents.constData(); for (int y = 0; y < size; y++) { bool difference = false; - for (int x = 0; x < size; x++, src += BYTES_PER_PIXEL, ref += BYTES_PER_PIXEL) { + for (int x = 0; x < size; x++, src += COLOR_BYTES, ref += COLOR_BYTES) { if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { minX = qMin(minX, x); maxX = qMax(maxX, x); @@ -594,9 +591,9 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r int width = qMax(maxX - minX + 1, 0); int height = qMax(maxY - minY + 1, 0); image = QImage(width, height, QImage::Format_RGB888); - src = _contents.constData() + (minY * size + minX) * BYTES_PER_PIXEL; - int srcStride = size * BYTES_PER_PIXEL; - int destStride = width * BYTES_PER_PIXEL; + src = _contents.constData() + (minY * size + minX) * COLOR_BYTES; + int srcStride = size * COLOR_BYTES; + int destStride = width * COLOR_BYTES; for (int y = 0; y < height; y++) { memcpy(image.scanLine(y), src, destStride); src += srcStride; @@ -652,14 +649,14 @@ void HeightfieldData::read(Bitstream& in, int bytes, bool color) { void HeightfieldData::set(const QImage& image, bool color) { if (color) { - _contents.resize(image.width() * image.height() * BYTES_PER_PIXEL); + _contents.resize(image.width() * image.height() * COLOR_BYTES); memcpy(_contents.data(), image.constBits(), _contents.size()); } else { _contents.resize(image.width() * image.height()); char* dest = _contents.data(); - for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL; - src != end; src += BYTES_PER_PIXEL) { + for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES; + src != end; src += COLOR_BYTES) { *dest++ = *src; } } @@ -856,8 +853,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); return true; } - int size = glm::sqrt(maxSize / (float)BYTES_PER_PIXEL); - QByteArray contents(size * size * BYTES_PER_PIXEL, 0); + int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES); + QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0); int halfSize = size / 2; for (int i = 0; i < MERGE_COUNT; i++) { HeightfieldDataPointer child = decodeInline(children[i]); @@ -865,7 +862,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post continue; } const QByteArray& childContents = child->getContents(); - int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL); + int childSize = glm::sqrt(childContents.size() / (float)HeightfieldData::COLOR_BYTES); const int INDEX_MASK = 1; int xIndex = i & INDEX_MASK; const int Y_SHIFT = 1; @@ -875,24 +872,25 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post } int Z_SHIFT = 2; int zIndex = (i >> Z_SHIFT) & INDEX_MASK; - char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL; + char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * HeightfieldData::COLOR_BYTES; uchar* src = (uchar*)childContents.data(); - int childStride = childSize * BYTES_PER_PIXEL; - int stride = size * BYTES_PER_PIXEL; + int childStride = childSize * HeightfieldData::COLOR_BYTES; + int stride = size * HeightfieldData::COLOR_BYTES; int halfStride = stride / 2; - int childStep = 2 * BYTES_PER_PIXEL; - int redOffset3 = childStride + BYTES_PER_PIXEL; - int greenOffset1 = BYTES_PER_PIXEL + 1; + int childStep = 2 * HeightfieldData::COLOR_BYTES; + int redOffset3 = childStride + HeightfieldData::COLOR_BYTES; + int greenOffset1 = HeightfieldData::COLOR_BYTES + 1; int greenOffset2 = childStride + 1; - int greenOffset3 = childStride + BYTES_PER_PIXEL + 1; - int blueOffset1 = BYTES_PER_PIXEL + 2; + int greenOffset3 = childStride + HeightfieldData::COLOR_BYTES + 1; + int blueOffset1 = HeightfieldData::COLOR_BYTES + 2; int blueOffset2 = childStride + 2; - int blueOffset3 = childStride + BYTES_PER_PIXEL + 2; + int blueOffset3 = childStride + HeightfieldData::COLOR_BYTES + 2; if (childSize == size) { // simple case: one destination value for four child values for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2; + for (char* end = dest + halfSize * HeightfieldData::COLOR_BYTES; dest != end; src += childStep) { + *dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] + + (int)src[childStride] + (int)src[redOffset3]) >> 2; *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; } @@ -904,13 +902,14 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post int halfChildSize = childSize / 2; int destPerSrc = size / childSize; for (int z = 0; z < halfChildSize; z++) { - for (uchar* end = src + childSize * BYTES_PER_PIXEL; src != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2; + for (uchar* end = src + childSize * HeightfieldData::COLOR_BYTES; src != end; src += childStep) { + *dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] + + (int)src[childStride] + (int)src[redOffset3]) >> 2; *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - BYTES_PER_PIXEL, BYTES_PER_PIXEL); - dest += BYTES_PER_PIXEL; + memcpy(dest, dest - HeightfieldData::COLOR_BYTES, HeightfieldData::COLOR_BYTES); + dest += HeightfieldData::COLOR_BYTES; } } dest += halfStride; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index d0f1cdf836..ddf6105662 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -428,6 +428,8 @@ typedef QExplicitlySharedDataPointer HeightfieldDataPointer; class HeightfieldData : public QSharedData { public: + static const int COLOR_BYTES = 3; + HeightfieldData(const QByteArray& contents); HeightfieldData(Bitstream& in, int bytes, bool color); HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color); From 21d6c3c48a3785ecbde01631e3cf9458682061ff Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 12 Aug 2014 18:21:04 -0700 Subject: [PATCH 05/10] More work on edge rejiggery. --- interface/src/MetavoxelSystem.cpp | 140 +++++++++++++++++++-- interface/src/MetavoxelSystem.h | 5 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 8 ++ libraries/metavoxels/src/MetavoxelUtil.h | 4 + 4 files changed, 148 insertions(+), 9 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9286b9037b..067c1984c6 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -672,25 +672,149 @@ HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) : class BorderFetchVisitor : public MetavoxelVisitor { public: - BorderFetchVisitor(const MetavoxelLOD& lod, QByteArray& height); + BorderFetchVisitor(const MetavoxelLOD& lod, HeightfieldBuffer* buffer); virtual int visit(MetavoxelInfo& info); private: - QByteArray& _height; + HeightfieldBuffer* _buffer; + Box _expandedBounds; + int _heightSize; + float _heightExtension; + int _colorSize; + float _colorExtension; + Box _colorBounds; }; -BorderFetchVisitor::BorderFetchVisitor(const MetavoxelLOD& lod, QByteArray& height) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), - QVector(), lod), - _height(height) { +BorderFetchVisitor::BorderFetchVisitor(const MetavoxelLOD& lod, HeightfieldBuffer* buffer) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << + AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector(), lod), + _buffer(buffer), + _expandedBounds(_buffer->getTranslation(), _buffer->getTranslation() + + glm::vec3(_buffer->getScale(), _buffer->getScale(), _buffer->getScale())), + _heightSize(glm::sqrt(buffer->getHeight().size())), + _heightExtension(_buffer->getScale() / (_heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)), + _colorSize(glm::sqrt(buffer->getColor().size() / HeightfieldData::COLOR_BYTES)), + _colorBounds(_expandedBounds) { + + _expandedBounds.minimum.x -= _heightExtension * HeightfieldBuffer::HEIGHT_BORDER; + _expandedBounds.minimum.z -= _heightExtension * HeightfieldBuffer::HEIGHT_BORDER; + _expandedBounds.maximum.x += _heightExtension * (HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE); + _expandedBounds.maximum.z += _heightExtension * (HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE); + + if (_colorSize > 0) { + _colorExtension = buffer->getScale() / (_colorSize - HeightfieldBuffer::SHARED_EDGE); + _colorBounds.maximum.x += _colorExtension * HeightfieldBuffer::SHARED_EDGE; + _colorBounds.maximum.z += _colorExtension * HeightfieldBuffer::SHARED_EDGE; + } } int BorderFetchVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { + Box bounds = info.getBounds(); + if (!bounds.intersects(_expandedBounds)) { + return STOP_RECURSION; + } + if (!info.isLeaf && info.size > _buffer->getScale()) { return DEFAULT_ORDER; } + if (_expandedBounds.contains(bounds)) { + return STOP_RECURSION; // this is the principal, which we've already filled in + } + HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); + if (!height) { + return STOP_RECURSION; + } + Box intersection = bounds.getIntersection(_expandedBounds); + int destX = glm::round((intersection.minimum.x - _expandedBounds.minimum.x) / _heightExtension); + int destY = glm::round((intersection.minimum.z - _expandedBounds.minimum.z) / _heightExtension); + int destWidth = glm::round((intersection.maximum.x - intersection.minimum.x) / _heightExtension); + int destHeight = glm::round((intersection.maximum.z - intersection.minimum.z) / _heightExtension); + char* dest = _buffer->getHeight().data() + destY * _heightSize + destX; + + const QByteArray& srcHeight = height->getContents(); + int srcSize = glm::sqrt(srcHeight.size()); + float srcExtension = info.size / srcSize; + + if (info.size == _buffer->getScale() && srcSize == (_heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { + // easy case: same resolution + int srcX = glm::round((intersection.minimum.x - info.minimum.x) / srcExtension); + int srcY = glm::round((intersection.minimum.z - info.minimum.z) / srcExtension); + + const char* src = srcHeight.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += _heightSize) { + memcpy(dest, src, destWidth); + } + } else { + // more difficult: different resolutions + float srcX = (intersection.minimum.x - info.minimum.x) / srcExtension; + float srcY = (intersection.minimum.z - info.minimum.z) / srcExtension; + float srcIncrement = _heightExtension / srcExtension; + int shift = 0; + float size = _buffer->getScale(); + while (size < info.size) { + shift++; + size *= 2.0f; + } + const int EIGHT_BIT_MAXIMUM = 255; + int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); + for (int y = 0; y < destHeight; y++, dest += _heightSize, srcY += srcIncrement) { + const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcIncrement) { + *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + } + } + } + + if (_colorSize == 0) { + return STOP_RECURSION; + } + HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); + if (!color) { + return STOP_RECURSION; + } + intersection = bounds.getIntersection(_colorBounds); + destX = glm::round((intersection.minimum.x - _colorBounds.minimum.x) / _colorExtension); + destY = glm::round((intersection.minimum.z - _colorBounds.minimum.z) / _colorExtension); + destWidth = glm::round((intersection.maximum.x - intersection.minimum.x) / _colorExtension); + destHeight = glm::round((intersection.maximum.z - intersection.minimum.z) / _colorExtension); + dest = _buffer->getColor().data() + (destY * _colorSize + destX) * HeightfieldData::COLOR_BYTES; + int destStride = _colorSize * HeightfieldData::COLOR_BYTES; + int destBytes = destWidth * HeightfieldData::COLOR_BYTES; + + const QByteArray& srcColor = color->getContents(); + srcSize = glm::sqrt(srcColor.size() / HeightfieldData::COLOR_BYTES); + int srcStride = srcSize * HeightfieldData::COLOR_BYTES; + srcExtension = info.size / srcSize; + + if (srcExtension == _colorExtension) { + // easy case: same resolution + int srcX = glm::round((intersection.minimum.x - info.minimum.x) / srcExtension); + int srcY = glm::round((intersection.minimum.z - info.minimum.z) / srcExtension); + + const char* src = srcColor.constData() + (srcY * srcSize + srcX) * HeightfieldData::COLOR_BYTES; + for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { + memcpy(dest, src, destBytes); + } + } else { + // more difficult: different resolutions + float srcX = (intersection.minimum.x - info.minimum.x) / srcExtension; + float srcY = (intersection.minimum.z - info.minimum.z) / srcExtension; + float srcIncrement = _colorExtension / srcExtension; + for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcIncrement) { + const char* src = srcColor.constData() + (int)srcY * srcStride; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += HeightfieldData::COLOR_BYTES, + lineSrcX += srcIncrement) { + const char* lineSrc = src + (int)lineSrcX * HeightfieldData::COLOR_BYTES; + lineDest[0] = lineSrc[0]; + lineDest[1] = lineSrc[1]; + lineDest[2] = lineSrc[2]; + } + } + } + return STOP_RECURSION; } @@ -724,6 +848,8 @@ int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { } } buffer = new HeightfieldBuffer(info.minimum, info.size, extendedHeightContents, extendedColorContents); + BorderFetchVisitor visitor(_lod, buffer); + _data->guide(visitor); } info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); return STOP_RECURSION; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index a35155dbd6..1a86778b08 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -139,9 +139,10 @@ public: ~HeightfieldBuffer(); const glm::vec3& getTranslation() const { return _translation; } + float getScale() const { return _scale; } - const QByteArray& getHeight() const { return _height; } - const QByteArray& getColor() const { return _color; } + QByteArray& getHeight() { return _height; } + QByteArray& getColor() { return _color; } QByteArray getUnextendedHeight() const; QByteArray getUnextendedColor() const; diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 2b46330961..4911c0e95f 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -182,6 +182,14 @@ bool Box::intersects(const Box& other) const { other.maximum.z >= minimum.z && other.minimum.z <= maximum.z; } +Box Box::getIntersection(const Box& other) const { + return Box(glm::max(minimum, other.minimum), glm::min(maximum, other.maximum)); +} + +bool Box::isEmpty() const { + return minimum.x >= maximum.x || minimum.y >= maximum.y || minimum.z >= maximum.z; +} + const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 339c07a21e..2b63b81adc 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -50,6 +50,10 @@ public: bool intersects(const Box& other) const; + Box getIntersection(const Box& other) const; + + bool isEmpty() const; + float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); } glm::vec3 getVertex(int index) const; From 56c6c3c972b50d053ad7b14939d78c7217ec059e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 13 Aug 2014 11:05:48 -0700 Subject: [PATCH 06/10] Working on the height/ray queries. --- interface/src/MetavoxelSystem.cpp | 268 +++++++++++++++++- interface/src/MetavoxelSystem.h | 7 + interface/src/ui/MetavoxelEditor.cpp | 11 +- .../metavoxels/src/MetavoxelClientManager.cpp | 252 ---------------- .../metavoxels/src/MetavoxelClientManager.h | 4 - 5 files changed, 283 insertions(+), 259 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 067c1984c6..238eda8784 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -118,6 +118,267 @@ void MetavoxelSystem::render() { guideToAugmented(renderVisitor); } +class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { +public: + + float intersectionDistance; + + RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); + + virtual int visit(MetavoxelInfo& info, float distance); +}; + +RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, + const glm::vec3& direction, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, QVector() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), + intersectionDistance(FLT_MAX) { +} + +static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f; +static const int HEIGHT_BORDER = 1; + +int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { + if (!info.isLeaf) { + return _order; + } + const HeightfieldBuffer* buffer = static_cast( + info.inputValues.at(0).getInlineValue().data()); + if (!buffer) { + return STOP_RECURSION; + } + const QByteArray& contents = buffer->getHeight(); + const uchar* src = (const uchar*)contents.constData(); + int size = glm::sqrt((float)contents.size()); + int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; + int highest = HEIGHT_BORDER + unextendedSize; + float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL; + + // find the initial location in heightfield coordinates + glm::vec3 entry = (_origin + distance * _direction - info.minimum + glm::vec3(HEIGHT_BORDER, 0.0f, HEIGHT_BORDER)) * + (float)unextendedSize / info.size; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (_direction.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (_direction.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + + bool withinBounds = true; + float accumulatedDistance = 0.0f; + while (withinBounds) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, HEIGHT_BORDER), highest); + int floorZ = qMin(qMax((int)floors.z, HEIGHT_BORDER), highest); + int ceilX = qMin(qMax((int)ceils.x, HEIGHT_BORDER), highest); + int ceilZ = qMin(qMax((int)ceils.z, HEIGHT_BORDER), highest); + float upperLeft = src[floorZ * size + floorX] * heightScale; + float upperRight = src[floorZ * size + ceilX] * heightScale; + float lowerLeft = src[ceilZ * size + floorX] * heightScale; + float lowerRight = src[ceilZ * size + ceilX] * heightScale; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (_direction.x > 0.0f) { + xDistance = (ceils.x - entry.x) / _direction.x; + } else if (_direction.x < 0.0f) { + xDistance = (floors.x - entry.x) / _direction.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (_direction.z > 0.0f) { + zDistance = (ceils.z - entry.z) / _direction.z; + } else if (_direction.z < 0.0f) { + zDistance = (floors.z - entry.z) / _direction.z; + } + + // the exit distance is the lower of those two + float exitDistance = qMin(xDistance, zDistance); + glm::vec3 exit, nextFloors = floors, nextCeils = ceils; + if (exitDistance == FLT_MAX) { + if (_direction.y > 0.0f) { + return SHORT_CIRCUIT; // line points upwards; no collisions possible + } + withinBounds = false; // line points downwards; check this cell only + + } else { + // find the exit point and the next cell, and determine whether it's still within the bounds + exit = entry + exitDistance * _direction; + withinBounds = (exit.y >= HEIGHT_BORDER && exit.y <= highest); + if (exitDistance == xDistance) { + if (_direction.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highest; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= HEIGHT_BORDER; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (_direction.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highest; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= HEIGHT_BORDER; + nextCeils.z -= 1.0f; + } + } + // check the vertical range of the ray against the ranges of the cell heights + if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || + qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + continue; + } + } + // having passed the bounds check, we must check against the planes + glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); + + // first check the triangle including the Z+ segment + glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); + float lowerProduct = glm::dot(lowerNormal, _direction); + if (lowerProduct < 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * _direction; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + intersectionDistance = qMin(intersectionDistance, distance + + (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); + return SHORT_CIRCUIT; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, _direction); + if (upperProduct < 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * _direction; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + intersectionDistance = qMin(intersectionDistance, distance + + (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); + return SHORT_CIRCUIT; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return STOP_RECURSION; +} + +bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin, + const glm::vec3& direction, float& distance) { + RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); + guideToAugmented(visitor); + if (visitor.intersectionDistance == FLT_MAX) { + return false; + } + distance = visitor.intersectionDistance; + return true; +} + +class HeightfieldHeightVisitor : public MetavoxelVisitor { +public: + + float height; + + HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); + + virtual int visit(MetavoxelInfo& info); + +private: + + glm::vec3 _location; +}; + +HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : + MetavoxelVisitor(QVector() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), + height(-FLT_MAX), + _location(location) { +} + +static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); + +int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { + glm::vec3 relative = _location - info.minimum; + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || + height >= info.minimum.y + info.size) { + return STOP_RECURSION; + } + if (!info.isLeaf) { + return REVERSE_ORDER; + } + const HeightfieldBuffer* buffer = static_cast( + info.inputValues.at(0).getInlineValue().data()); + if (!buffer) { + return STOP_RECURSION; + } + const QByteArray& contents = buffer->getHeight(); + const uchar* src = (const uchar*)contents.constData(); + int size = glm::sqrt((float)contents.size()); + int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; + int highest = HEIGHT_BORDER + unextendedSize; + relative *= unextendedSize / info.size; + relative.x += HEIGHT_BORDER; + relative.z += HEIGHT_BORDER; + + // find the bounds of the cell containing the point and the shared vertex heights + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = qMin(qMax((int)floors.x, HEIGHT_BORDER), highest); + int floorZ = qMin(qMax((int)floors.z, HEIGHT_BORDER), highest); + int ceilX = qMin(qMax((int)ceils.x, HEIGHT_BORDER), highest); + int ceilZ = qMin(qMax((int)ceils.z, HEIGHT_BORDER), highest); + float upperLeft = src[floorZ * size + floorX]; + float lowerRight = src[ceilZ * size + ceilX]; + float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + float upperRight = src[floorZ * size + ceilX]; + interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), + (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + float lowerLeft = src[ceilZ * size + floorX]; + interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); + } + if (interpolatedHeight == 0.0f) { + return STOP_RECURSION; // ignore zero values + } + + // convert the interpolated height into world space + height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); + return SHORT_CIRCUIT; +} + +float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { + HeightfieldHeightVisitor visitor(getLOD(), location); + guideToAugmented(visitor); + return visitor.height; +} + class HeightfieldCursorRenderVisitor : public MetavoxelVisitor { public: @@ -416,7 +677,7 @@ void HeightfieldBuffer::render(bool cursor) { int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER; int vertexCount = _heightSize * _heightSize; int rows = _heightSize - 1; - int indexCount = rows * rows * 4; + int indexCount = rows * rows * 3 * 2; BufferPair& bufferPair = _bufferPairs[_heightSize]; if (!bufferPair.first.isCreated()) { QVector vertices(vertexCount); @@ -450,8 +711,11 @@ void HeightfieldBuffer::render(bool cursor) { for (int j = 0; j < rows; j++) { *index++ = lineIndex + j; *index++ = nextLineIndex + j; + *index++ = nextLineIndex + j + 1; + *index++ = nextLineIndex + j + 1; *index++ = lineIndex + j + 1; + *index++ = lineIndex + j; } } @@ -484,7 +748,7 @@ void HeightfieldBuffer::render(bool cursor) { glBindTexture(GL_TEXTURE_2D, _colorTextureID); } - glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); if (!cursor) { glBindTexture(GL_TEXTURE_2D, 0); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 1a86778b08..fb18d4f402 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -45,6 +45,10 @@ public: void renderHeightfieldCursor(const glm::vec3& position, float radius); + bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); + + Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); + Q_INVOKABLE void deleteTextures(int heightID, int colorID); protected: @@ -142,7 +146,10 @@ public: float getScale() const { return _scale; } QByteArray& getHeight() { return _height; } + const QByteArray& getHeight() const { return _height; } + QByteArray& getColor() { return _color; } + const QByteArray& getColor() const { return _color; } QByteArray getUnextendedHeight() const; QByteArray getUnextendedColor() const; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b7057532fb..7c5e84132b 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1101,7 +1101,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin } void HeightfieldBrushTool::render() { - if (Application::getInstance()->isMouseHidden()) { + if (Application::getInstance()->isMouseHidden() && false) { return; } @@ -1115,6 +1115,15 @@ void HeightfieldBrushTool::render() { } Application::getInstance()->getMetavoxels()->renderHeightfieldCursor( _position = origin + distance * direction, _radius->value()); + + glPushMatrix(); + glTranslatef(_position.x, _position.y, _position.z); + + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + + glutSolidSphere(1.0f, 10, 10); + + glPopMatrix(); } bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) { diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 70b574d2a2..a2d3410314 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -61,180 +61,6 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } -class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { -public: - - float intersectionDistance; - - RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info, float distance); -}; - -RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, - const glm::vec3& direction, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, QVector() << - AttributeRegistry::getInstance()->getHeightfieldAttribute(), QVector(), lod), - intersectionDistance(FLT_MAX) { -} - -static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f; - -int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { - if (!info.isLeaf) { - return _order; - } - HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue(); - if (!pointer) { - return STOP_RECURSION; - } - const QByteArray& contents = pointer->getContents(); - const uchar* src = (const uchar*)contents.constData(); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = highest * EIGHT_BIT_MAXIMUM_RECIPROCAL; - - // find the initial location in heightfield coordinates - glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)highest / info.size; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (_direction.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (_direction.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - bool withinBounds = true; - float accumulatedDistance = 0.0f; - while (withinBounds) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, 0), highest); - int floorZ = qMin(qMax((int)floors.z, 0), highest); - int ceilX = qMin(qMax((int)ceils.x, 0), highest); - int ceilZ = qMin(qMax((int)ceils.z, 0), highest); - float upperLeft = src[floorZ * size + floorX] * heightScale; - float upperRight = src[floorZ * size + ceilX] * heightScale; - float lowerLeft = src[ceilZ * size + floorX] * heightScale; - float lowerRight = src[ceilZ * size + ceilX] * heightScale; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (_direction.x > 0.0f) { - xDistance = (ceils.x - entry.x) / _direction.x; - } else if (_direction.x < 0.0f) { - xDistance = (floors.x - entry.x) / _direction.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (_direction.z > 0.0f) { - zDistance = (ceils.z - entry.z) / _direction.z; - } else if (_direction.z < 0.0f) { - zDistance = (floors.z - entry.z) / _direction.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - if (_direction.y > 0.0f) { - return SHORT_CIRCUIT; // line points upwards; no collisions possible - } - withinBounds = false; // line points downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * _direction; - withinBounds = (exit.y >= 0.0f && exit.y <= highest); - if (exitDistance == xDistance) { - if (_direction.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highest; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (_direction.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highest; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, _direction); - if (lowerProduct < 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * _direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - intersectionDistance = qMin(intersectionDistance, distance + - (accumulatedDistance + planeDistance) * (info.size / highest)); - return SHORT_CIRCUIT; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, _direction); - if (upperProduct < 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * _direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - intersectionDistance = qMin(intersectionDistance, distance + - (accumulatedDistance + planeDistance) * (info.size / highest)); - return SHORT_CIRCUIT; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return STOP_RECURSION; -} - -bool MetavoxelClientManager::findFirstRayHeightfieldIntersection(const glm::vec3& origin, - const glm::vec3& direction, float& distance) { - RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); - guide(visitor); - if (visitor.intersectionDistance == FLT_MAX) { - return false; - } - distance = visitor.intersectionDistance; - return true; -} - void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) { Sphere* sphere = new Sphere(); sphere->setTranslation(center); @@ -252,84 +78,6 @@ void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool re QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); } -class HeightfieldHeightVisitor : public MetavoxelVisitor { -public: - - float height; - - HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); - - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _location; -}; - -HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), - QVector(), lod), - height(-FLT_MAX), - _location(location) { -} - -static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); - -int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { - glm::vec3 relative = _location - info.minimum; - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || - height >= info.minimum.y + info.size) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return REVERSE_ORDER; - } - HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue(); - if (!pointer) { - return STOP_RECURSION; - } - const QByteArray& contents = pointer->getContents(); - const uchar* src = (const uchar*)contents.constData(); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - relative *= highest / info.size; - - // find the bounds of the cell containing the point and the shared vertex heights - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = qMin(qMax((int)floors.x, 0), highest); - int floorZ = qMin(qMax((int)floors.z, 0), highest); - int ceilX = qMin(qMax((int)ceils.x, 0), highest); - int ceilZ = qMin(qMax((int)ceils.z, 0), highest); - float upperLeft = src[floorZ * size + floorX]; - float lowerRight = src[ceilZ * size + ceilX]; - float interpolatedHeight; - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x > fracts.z) { - float upperRight = src[floorZ * size + ceilX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, upperRight, fracts.x), lowerRight, fracts.z); - - } else { - float lowerLeft = src[ceilZ * size + floorX]; - interpolatedHeight = glm::mix(upperLeft, glm::mix(lowerLeft, lowerRight, fracts.x), fracts.z); - } - if (interpolatedHeight == 0.0f) { - return STOP_RECURSION; // ignore zero values - } - - // convert the interpolated height into world space - height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); - return SHORT_CIRCUIT; -} - -float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) { - HeightfieldHeightVisitor visitor(getLOD(), location); - guide(visitor); - return visitor.height; -} - MetavoxelLOD MetavoxelClientManager::getLOD() { return MetavoxelLOD(); } diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 8fc9bcd38d..333b709b9e 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -37,16 +37,12 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); - bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, const QColor& color = QColor(Qt::gray)); Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); - Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); - /// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread. virtual MetavoxelLOD getLOD(); From 8c42a3a8485fa21327ff8ec10a20df474d41c7e3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 13 Aug 2014 11:30:05 -0700 Subject: [PATCH 07/10] Ray query fix. --- interface/src/MetavoxelSystem.cpp | 40 +++++++++++++++------------- interface/src/MetavoxelSystem.h | 6 ++--- interface/src/ui/MetavoxelEditor.cpp | 11 +------- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 238eda8784..a5f5de5123 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -136,7 +136,6 @@ RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm:: } static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f; -static const int HEIGHT_BORDER = 1; int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { if (!info.isLeaf) { @@ -151,12 +150,13 @@ int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance const uchar* src = (const uchar*)contents.constData(); int size = glm::sqrt((float)contents.size()); int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; - int highest = HEIGHT_BORDER + unextendedSize; + int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL; // find the initial location in heightfield coordinates - glm::vec3 entry = (_origin + distance * _direction - info.minimum + glm::vec3(HEIGHT_BORDER, 0.0f, HEIGHT_BORDER)) * - (float)unextendedSize / info.size; + glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size; + entry.x += HeightfieldBuffer::HEIGHT_BORDER; + entry.z += HeightfieldBuffer::HEIGHT_BORDER; glm::vec3 floors = glm::floor(entry); glm::vec3 ceils = glm::ceil(entry); if (floors.x == ceils.x) { @@ -178,10 +178,10 @@ int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance float accumulatedDistance = 0.0f; while (withinBounds) { // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, HEIGHT_BORDER), highest); - int floorZ = qMin(qMax((int)floors.z, HEIGHT_BORDER), highest); - int ceilX = qMin(qMax((int)ceils.x, HEIGHT_BORDER), highest); - int ceilZ = qMin(qMax((int)ceils.z, HEIGHT_BORDER), highest); + int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); + int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); float upperLeft = src[floorZ * size + floorX] * heightScale; float upperRight = src[floorZ * size + ceilX] * heightScale; float lowerLeft = src[ceilZ * size + floorX] * heightScale; @@ -215,13 +215,13 @@ int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance } else { // find the exit point and the next cell, and determine whether it's still within the bounds exit = entry + exitDistance * _direction; - withinBounds = (exit.y >= HEIGHT_BORDER && exit.y <= highest); + withinBounds = (exit.y >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest); if (exitDistance == xDistance) { if (_direction.x > 0.0f) { nextFloors.x += 1.0f; withinBounds &= (nextCeils.x += 1.0f) <= highest; } else { - withinBounds &= (nextFloors.x -= 1.0f) >= HEIGHT_BORDER; + withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; nextCeils.x -= 1.0f; } } @@ -230,7 +230,7 @@ int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance nextFloors.z += 1.0f; withinBounds &= (nextCeils.z += 1.0f) <= highest; } else { - withinBounds &= (nextFloors.z -= 1.0f) >= HEIGHT_BORDER; + withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; nextCeils.z -= 1.0f; } } @@ -337,19 +337,19 @@ int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { const uchar* src = (const uchar*)contents.constData(); int size = glm::sqrt((float)contents.size()); int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; - int highest = HEIGHT_BORDER + unextendedSize; + int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; relative *= unextendedSize / info.size; - relative.x += HEIGHT_BORDER; - relative.z += HEIGHT_BORDER; + relative.x += HeightfieldBuffer::HEIGHT_BORDER; + relative.z += HeightfieldBuffer::HEIGHT_BORDER; // find the bounds of the cell containing the point and the shared vertex heights glm::vec3 floors = glm::floor(relative); glm::vec3 ceils = glm::ceil(relative); glm::vec3 fracts = glm::fract(relative); - int floorX = qMin(qMax((int)floors.x, HEIGHT_BORDER), highest); - int floorZ = qMin(qMax((int)floors.z, HEIGHT_BORDER), highest); - int ceilX = qMin(qMax((int)ceils.x, HEIGHT_BORDER), highest); - int ceilZ = qMin(qMax((int)ceils.z, HEIGHT_BORDER), highest); + int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); + int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); float upperLeft = src[floorZ * size + floorX]; float lowerRight = src[ceilZ * size + ceilX]; float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); @@ -593,6 +593,10 @@ void PointBuffer::render(bool cursor) { _buffer.release(); } +const int HeightfieldBuffer::HEIGHT_BORDER = 1; +const int HeightfieldBuffer::SHARED_EDGE = 1; +const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE; + HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color) : _translation(translation), diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index fb18d4f402..39136cb1f4 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -135,9 +135,9 @@ private: class HeightfieldBuffer : public BufferData { public: - static const int HEIGHT_BORDER = 1; - static const int SHARED_EDGE = 1; - static const int HEIGHT_EXTENSION = 2 * HEIGHT_BORDER + SHARED_EDGE; + static const int HEIGHT_BORDER; + static const int SHARED_EDGE; + static const int HEIGHT_EXTENSION; HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color); ~HeightfieldBuffer(); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 7c5e84132b..b7057532fb 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1101,7 +1101,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin } void HeightfieldBrushTool::render() { - if (Application::getInstance()->isMouseHidden() && false) { + if (Application::getInstance()->isMouseHidden()) { return; } @@ -1115,15 +1115,6 @@ void HeightfieldBrushTool::render() { } Application::getInstance()->getMetavoxels()->renderHeightfieldCursor( _position = origin + distance * direction, _radius->value()); - - glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - - glColor4f(1.0f, 0.0f, 0.0f, 1.0f); - - glutSolidSphere(1.0f, 10, 10); - - glPopMatrix(); } bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) { From 62b84edc7f3357f8f6794eea6b6b590597fd9e39 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 13 Aug 2014 15:13:06 -0700 Subject: [PATCH 08/10] Brush fix. --- libraries/metavoxels/src/MetavoxelMessages.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index ad3c36eb03..df6e8172e4 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -361,7 +361,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { QByteArray contents(pointer->getContents()); int size = glm::sqrt((float)contents.size()); int highest = size - 1; - float heightScale = highest / info.size; + float heightScale = size / info.size; glm::vec3 center = (_edit.position - info.minimum) * heightScale; float scaledRadius = _edit.radius * heightScale; @@ -450,7 +450,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { 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; + float heightScale = size / info.size; glm::vec3 center = (_edit.position - info.minimum) * heightScale; float scaledRadius = _edit.radius * heightScale; From 05d6b628fa7defff44195163811b0ce309f2f1f8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 14 Aug 2014 17:10:45 -0700 Subject: [PATCH 09/10] Alllmost ready with the big edge sharing rejiggery. --- interface/src/MetavoxelSystem.cpp | 463 +++++++++++------- interface/src/MetavoxelSystem.h | 16 + .../metavoxels/src/AttributeRegistry.cpp | 2 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 5 + libraries/metavoxels/src/MetavoxelUtil.h | 2 + 5 files changed, 304 insertions(+), 184 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a5f5de5123..40a2ff0097 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -37,6 +37,9 @@ void MetavoxelSystem::init() { _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("heightfieldBuffer")); + + _heightfieldBufferAttribute->setLODThresholdMultiplier( + AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); } MetavoxelLOD MetavoxelSystem::getLOD() { @@ -382,7 +385,7 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { class HeightfieldCursorRenderVisitor : public MetavoxelVisitor { public: - HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds); + HeightfieldCursorRenderVisitor(const Box& bounds); virtual int visit(MetavoxelInfo& info); @@ -391,9 +394,9 @@ private: Box _bounds; }; -HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds) : +HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) : MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()), _bounds(bounds) { } @@ -433,7 +436,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - HeightfieldCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents)); + HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents)); guideToAugmented(visitor); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); @@ -601,11 +604,24 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color) : _translation(translation), _scale(scale), + _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), + _colorBounds(_heightBounds), _height(height), _color(color), _heightTextureID(0), _colorTextureID(0), - _heightSize(glm::sqrt(height.size())) { + _heightSize(glm::sqrt(height.size())), + _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), + _colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)), + _colorIncrement(scale / (_colorSize - SHARED_EDGE)) { + + _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; + _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; + _heightBounds.maximum.x += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER); + _heightBounds.maximum.z += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER); + + _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; + _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { @@ -807,17 +823,19 @@ BufferDataAttribute::BufferDataAttribute(const QString& name) : } bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const { - BufferDataPointer firstChild = decodeInline(children[0]); - for (int i = 1; i < MERGE_COUNT; i++) { - if (firstChild != decodeInline(children[i])) { - *(BufferDataPointer*)&parent = _defaultValue; + *(BufferDataPointer*)&parent = _defaultValue; + for (int i = 0; i < MERGE_COUNT; i++) { + if (decodeInline(children[i])) { return false; } } - *(BufferDataPointer*)&parent = firstChild; return true; } +AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + void DefaultMetavoxelRendererImplementation::init() { if (!_pointProgram.isLinked()) { _pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); @@ -923,206 +941,279 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { return true; } -class HeightfieldAugmentVisitor : public MetavoxelVisitor { -public: - - HeightfieldAugmentVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); -}; - -HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) { -} - -class BorderFetchVisitor : public MetavoxelVisitor { +class HeightfieldFetchVisitor : public MetavoxelVisitor { public: - BorderFetchVisitor(const MetavoxelLOD& lod, HeightfieldBuffer* buffer); + HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections); + + void init(HeightfieldBuffer* buffer) { _buffer = buffer; } virtual int visit(MetavoxelInfo& info); private: + const QVector& _intersections; HeightfieldBuffer* _buffer; - Box _expandedBounds; - int _heightSize; - float _heightExtension; - int _colorSize; - float _colorExtension; - Box _colorBounds; }; -BorderFetchVisitor::BorderFetchVisitor(const MetavoxelLOD& lod, HeightfieldBuffer* buffer) : +HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector(), lod), - _buffer(buffer), - _expandedBounds(_buffer->getTranslation(), _buffer->getTranslation() + - glm::vec3(_buffer->getScale(), _buffer->getScale(), _buffer->getScale())), - _heightSize(glm::sqrt(buffer->getHeight().size())), - _heightExtension(_buffer->getScale() / (_heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)), - _colorSize(glm::sqrt(buffer->getColor().size() / HeightfieldData::COLOR_BYTES)), - _colorBounds(_expandedBounds) { - - _expandedBounds.minimum.x -= _heightExtension * HeightfieldBuffer::HEIGHT_BORDER; - _expandedBounds.minimum.z -= _heightExtension * HeightfieldBuffer::HEIGHT_BORDER; - _expandedBounds.maximum.x += _heightExtension * (HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE); - _expandedBounds.maximum.z += _heightExtension * (HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE); - - if (_colorSize > 0) { - _colorExtension = buffer->getScale() / (_colorSize - HeightfieldBuffer::SHARED_EDGE); - _colorBounds.maximum.x += _colorExtension * HeightfieldBuffer::SHARED_EDGE; - _colorBounds.maximum.z += _colorExtension * HeightfieldBuffer::SHARED_EDGE; - } + _intersections(intersections) { } -int BorderFetchVisitor::visit(MetavoxelInfo& info) { +int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { Box bounds = info.getBounds(); - if (!bounds.intersects(_expandedBounds)) { + const Box& heightBounds = _buffer->getHeightBounds(); + if (!bounds.intersects(heightBounds)) { return STOP_RECURSION; } if (!info.isLeaf && info.size > _buffer->getScale()) { return DEFAULT_ORDER; } - if (_expandedBounds.contains(bounds)) { - return STOP_RECURSION; // this is the principal, which we've already filled in - } HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); if (!height) { return STOP_RECURSION; } - Box intersection = bounds.getIntersection(_expandedBounds); - int destX = glm::round((intersection.minimum.x - _expandedBounds.minimum.x) / _heightExtension); - int destY = glm::round((intersection.minimum.z - _expandedBounds.minimum.z) / _heightExtension); - int destWidth = glm::round((intersection.maximum.x - intersection.minimum.x) / _heightExtension); - int destHeight = glm::round((intersection.maximum.z - intersection.minimum.z) / _heightExtension); - char* dest = _buffer->getHeight().data() + destY * _heightSize + destX; - - const QByteArray& srcHeight = height->getContents(); - int srcSize = glm::sqrt(srcHeight.size()); - float srcExtension = info.size / srcSize; - - if (info.size == _buffer->getScale() && srcSize == (_heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { - // easy case: same resolution - int srcX = glm::round((intersection.minimum.x - info.minimum.x) / srcExtension); - int srcY = glm::round((intersection.minimum.z - info.minimum.z) / srcExtension); + foreach (const Box& intersection, _intersections) { + Box overlap = intersection.getIntersection(bounds); + if (overlap.isEmpty()) { + continue; + } + float heightIncrement = _buffer->getHeightIncrement(); + int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; + int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); + int heightSize = _buffer->getHeightSize(); + char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - const char* src = srcHeight.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += _heightSize) { - memcpy(dest, src, destWidth); + const QByteArray& srcHeight = height->getContents(); + int srcSize = glm::sqrt(srcHeight.size()); + float srcIncrement = info.size / srcSize; + + if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const char* src = srcHeight.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { + memcpy(dest, src, destWidth); + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = heightIncrement / srcIncrement; + int shift = 0; + float size = _buffer->getScale(); + while (size < info.size) { + shift++; + size *= 2.0f; + } + const int EIGHT_BIT_MAXIMUM = 255; + int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); + for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) { + const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { + *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + } + } } - } else { - // more difficult: different resolutions - float srcX = (intersection.minimum.x - info.minimum.x) / srcExtension; - float srcY = (intersection.minimum.z - info.minimum.z) / srcExtension; - float srcIncrement = _heightExtension / srcExtension; - int shift = 0; - float size = _buffer->getScale(); - while (size < info.size) { - shift++; - size *= 2.0f; + + int colorSize = _buffer->getColorSize(); + if (colorSize == 0) { + return STOP_RECURSION; } - const int EIGHT_BIT_MAXIMUM = 255; - int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); - for (int y = 0; y < destHeight; y++, dest += _heightSize, srcY += srcIncrement) { - const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcIncrement) { - *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); + if (!color) { + return STOP_RECURSION; + } + const Box& colorBounds = _buffer->getColorBounds(); + overlap = colorBounds.getIntersection(overlap); + float colorIncrement = _buffer->getColorIncrement(); + destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; + destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; + destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); + destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); + dest = _buffer->getColor().data() + (destY * colorSize + destX) * HeightfieldData::COLOR_BYTES; + int destStride = colorSize * HeightfieldData::COLOR_BYTES; + int destBytes = destWidth * HeightfieldData::COLOR_BYTES; + + const QByteArray& srcColor = color->getContents(); + srcSize = glm::sqrt(srcColor.size() / HeightfieldData::COLOR_BYTES); + int srcStride = srcSize * HeightfieldData::COLOR_BYTES; + srcIncrement = info.size / srcSize; + + if (srcIncrement == colorIncrement) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const char* src = srcColor.constData() + (srcY * srcSize + srcX) * HeightfieldData::COLOR_BYTES; + for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { + memcpy(dest, src, destBytes); + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = colorIncrement / srcIncrement; + for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) { + const char* src = srcColor.constData() + (int)srcY * srcStride; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += HeightfieldData::COLOR_BYTES, + lineSrcX += srcAdvance) { + const char* lineSrc = src + (int)lineSrcX * HeightfieldData::COLOR_BYTES; + lineDest[0] = lineSrc[0]; + lineDest[1] = lineSrc[1]; + lineDest[2] = lineSrc[2]; + } } } } - - if (_colorSize == 0) { - return STOP_RECURSION; - } - HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); - if (!color) { - return STOP_RECURSION; - } - intersection = bounds.getIntersection(_colorBounds); - destX = glm::round((intersection.minimum.x - _colorBounds.minimum.x) / _colorExtension); - destY = glm::round((intersection.minimum.z - _colorBounds.minimum.z) / _colorExtension); - destWidth = glm::round((intersection.maximum.x - intersection.minimum.x) / _colorExtension); - destHeight = glm::round((intersection.maximum.z - intersection.minimum.z) / _colorExtension); - dest = _buffer->getColor().data() + (destY * _colorSize + destX) * HeightfieldData::COLOR_BYTES; - int destStride = _colorSize * HeightfieldData::COLOR_BYTES; - int destBytes = destWidth * HeightfieldData::COLOR_BYTES; - - const QByteArray& srcColor = color->getContents(); - srcSize = glm::sqrt(srcColor.size() / HeightfieldData::COLOR_BYTES); - int srcStride = srcSize * HeightfieldData::COLOR_BYTES; - srcExtension = info.size / srcSize; - - if (srcExtension == _colorExtension) { - // easy case: same resolution - int srcX = glm::round((intersection.minimum.x - info.minimum.x) / srcExtension); - int srcY = glm::round((intersection.minimum.z - info.minimum.z) / srcExtension); - - const char* src = srcColor.constData() + (srcY * srcSize + srcX) * HeightfieldData::COLOR_BYTES; - for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { - memcpy(dest, src, destBytes); - } - } else { - // more difficult: different resolutions - float srcX = (intersection.minimum.x - info.minimum.x) / srcExtension; - float srcY = (intersection.minimum.z - info.minimum.z) / srcExtension; - float srcIncrement = _colorExtension / srcExtension; - for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcIncrement) { - const char* src = srcColor.constData() + (int)srcY * srcStride; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += HeightfieldData::COLOR_BYTES, - lineSrcX += srcIncrement) { - const char* lineSrc = src + (int)lineSrcX * HeightfieldData::COLOR_BYTES; - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; - } - } - } - return STOP_RECURSION; } -int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { - if (info.isLeaf) { - HeightfieldBuffer* buffer = NULL; - HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); - if (height) { - const QByteArray& heightContents = height->getContents(); - int size = glm::sqrt(heightContents.size()); - int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION; - QByteArray extendedHeightContents(extendedSize * extendedSize, 0); - char* dest = extendedHeightContents.data() + (extendedSize + 1) * HeightfieldBuffer::HEIGHT_BORDER; - const char* src = heightContents.constData(); - for (int z = 0; z < size; z++, src += size, dest += extendedSize) { - memcpy(dest, src, size); - } - QByteArray extendedColorContents; - HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color) { - const QByteArray& colorContents = color->getContents(); - int colorSize = glm::sqrt(colorContents.size() / HeightfieldData::COLOR_BYTES); - int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE; - extendedColorContents = QByteArray(extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES, 0); - char* dest = extendedColorContents.data(); - const char* src = colorContents.constData(); - int srcStride = colorSize * HeightfieldData::COLOR_BYTES; - int destStride = extendedColorSize * HeightfieldData::COLOR_BYTES; - for (int z = 0; z < colorSize; z++, src += srcStride, dest += destStride) { - memcpy(dest, src, srcStride); - } - } - buffer = new HeightfieldBuffer(info.minimum, info.size, extendedHeightContents, extendedColorContents); - BorderFetchVisitor visitor(_lod, buffer); - _data->guide(visitor); +class HeightfieldRegionVisitor : public MetavoxelVisitor { +public: + + QVector regions; + Box regionBounds; + + HeightfieldRegionVisitor(const MetavoxelLOD& lod); + + virtual int visit(MetavoxelInfo& info); + +private: + + void addRegion(const Box& unextended, const Box& extended); + + QVector _intersections; + HeightfieldFetchVisitor _fetchVisitor; +}; + +HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << + AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), + regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)), + _fetchVisitor(lod, _intersections) { +} + +int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + HeightfieldBuffer* buffer = NULL; + HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); + if (height) { + const QByteArray& heightContents = height->getContents(); + int size = glm::sqrt(heightContents.size()); + int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION; + int heightContentsSize = extendedSize * extendedSize; + + HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); + int colorContentsSize = 0; + if (color) { + const QByteArray& colorContents = color->getContents(); + int colorSize = glm::sqrt(colorContents.size() / HeightfieldData::COLOR_BYTES); + int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE; + colorContentsSize = extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES; } - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); + + const HeightfieldBuffer* existingBuffer = static_cast( + info.inputValues.at(2).getInlineValue().data()); + Box bounds = info.getBounds(); + if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && + existingBuffer->getColor().size() == colorContentsSize) { + // we already have a buffer of the correct resolution + addRegion(bounds, existingBuffer->getHeightBounds()); + return STOP_RECURSION; + } + // we must create a new buffer and update its borders + buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), + QByteArray(colorContentsSize, 0)); + const Box& heightBounds = buffer->getHeightBounds(); + addRegion(bounds, heightBounds); + + _intersections.clear(); + _intersections.append(Box(heightBounds.minimum, + glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); + _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), + glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); + _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), + heightBounds.maximum)); + _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), + glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); + + _fetchVisitor.init(buffer); + _data->guide(_fetchVisitor); + } + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); + return STOP_RECURSION; +} + +void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { + regions.append(unextended); + regionBounds.add(extended); +} + +class HeightfieldUpdateVisitor : public MetavoxelVisitor { +public: + + HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector& regions, const Box& regionBounds); + + virtual int visit(MetavoxelInfo& info); + +private: + + const QVector& _regions; + const Box& _regionBounds; + QVector _intersections; + HeightfieldFetchVisitor _fetchVisitor; +}; + +HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector& regions, + const Box& regionBounds) : + MetavoxelVisitor(QVector() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), + _regions(regions), + _regionBounds(regionBounds), + _fetchVisitor(lod, _intersections) { +} + +int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_regionBounds)) { return STOP_RECURSION; } - return DEFAULT_ORDER; + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + const HeightfieldBuffer* buffer = static_cast( + info.inputValues.at(0).getInlineValue().data()); + if (!buffer) { + return STOP_RECURSION; + } + _intersections.clear(); + foreach (const Box& region, _regions) { + if (region.intersects(buffer->getHeightBounds())) { + _intersections.append(region.getIntersection(buffer->getHeightBounds())); + } + } + if (_intersections.isEmpty()) { + return STOP_RECURSION; + } + HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, + buffer->getHeight(), buffer->getColor()); + _fetchVisitor.init(newBuffer); + _data->guide(_fetchVisitor); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer))); + return STOP_RECURSION; } void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, @@ -1149,8 +1240,12 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const PointAugmentVisitor pointAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, pointAugmentVisitor); - HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod); - data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor); + HeightfieldRegionVisitor heightfieldRegionVisitor(lod); + data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor); + + HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions, + heightfieldRegionVisitor.regionBounds); + data.guide(heightfieldUpdateVisitor); } class SpannerSimulateVisitor : public SpannerVisitor { @@ -1222,7 +1317,7 @@ bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, class BufferRenderVisitor : public MetavoxelVisitor { public: - BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod); + BufferRenderVisitor(const AttributePointer& attribute); virtual int visit(MetavoxelInfo& info); @@ -1232,8 +1327,8 @@ private: int _containmentDepth; }; -BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << attribute, QVector(), lod), +BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) : + MetavoxelVisitor(QVector() << attribute), _order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } @@ -1247,11 +1342,14 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) { } _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX; } + if (!info.isLeaf) { + return _order; + } BufferDataPointer buffer = info.inputValues.at(0).getInlineValue(); if (buffer) { buffer->render(); } - return info.isLeaf ? STOP_RECURSION : _order; + return STOP_RECURSION; } void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { @@ -1280,7 +1378,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glDisable(GL_BLEND); - BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod); + BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute()); data.guide(pointRenderVisitor); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); @@ -1300,8 +1398,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glEnableClientState(GL_TEXTURE_COORD_ARRAY); - BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), - lod); + BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); data.guide(heightfieldRenderVisitor); _heightfieldProgram.release(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 39136cb1f4..051c5da711 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -145,6 +145,9 @@ public: const glm::vec3& getTranslation() const { return _translation; } float getScale() const { return _scale; } + const Box& getHeightBounds() const { return _heightBounds; } + const Box& getColorBounds() const { return _colorBounds; } + QByteArray& getHeight() { return _height; } const QByteArray& getHeight() const { return _height; } @@ -154,17 +157,28 @@ public: QByteArray getUnextendedHeight() const; QByteArray getUnextendedColor() const; + int getHeightSize() const { return _heightSize; } + float getHeightIncrement() const { return _heightIncrement; } + + int getColorSize() const { return _colorSize; } + float getColorIncrement() const { return _colorIncrement; } + virtual void render(bool cursor = false); private: glm::vec3 _translation; float _scale; + Box _heightBounds; + Box _colorBounds; QByteArray _height; QByteArray _color; GLuint _heightTextureID; GLuint _colorTextureID; int _heightSize; + float _heightIncrement; + int _colorSize; + float _colorIncrement; typedef QPair BufferPair; static QHash _bufferPairs; @@ -193,6 +207,8 @@ public: Q_INVOKABLE BufferDataAttribute(const QString& name = QString()); virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; /// Renders metavoxels as points. diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 0f974b82df..14d05d6e9c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -55,7 +55,7 @@ AttributeRegistry::AttributeRegistry() : const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); - const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; + const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 40.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); } diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 4911c0e95f..e6b96e97b0 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -164,6 +164,11 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) : minimum(minimum), maximum(maximum) { } +void Box::add(const Box& other) { + minimum = glm::min(minimum, other.minimum); + maximum = glm::max(maximum, other.maximum); +} + bool Box::contains(const glm::vec3& point) const { return point.x >= minimum.x && point.x <= maximum.x && point.y >= minimum.y && point.y <= maximum.y && diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 2b63b81adc..4228af059f 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -44,6 +44,8 @@ public: explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); + void add(const Box& other); + bool contains(const glm::vec3& point) const; bool contains(const Box& other) const; From 67266b41dffdb8312c13fd3f4e41fd3cdba2c81d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 14 Aug 2014 19:21:32 -0700 Subject: [PATCH 10/10] Deflated JPEG encoding for blocks above 16x16. --- .../metavoxels/src/AttributeRegistry.cpp | 104 +++++++++++++----- 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 14d05d6e9c..1e30aee576 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -55,7 +55,7 @@ AttributeRegistry::AttributeRegistry() : const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); - const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 40.0f; + const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); } @@ -493,6 +493,49 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { read(in, bytes, color); } +enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; + +static QByteArray encodeHeightfieldImage(const QImage& image) { + if (image.isNull()) { + return QByteArray(1, NULL_HEIGHTFIELD_IMAGE); + } + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + const int JPEG_ENCODE_THRESHOLD = 16; + if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD) { + qint32 offsetX = image.offset().x(), offsetY = image.offset().y(); + buffer.write((char*)&offsetX, sizeof(qint32)); + buffer.write((char*)&offsetY, sizeof(qint32)); + image.save(&buffer, "JPG"); + return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data()); + + } else { + buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE); + image.save(&buffer, "PNG"); + return buffer.data(); + } +} + +const QImage decodeHeightfieldImage(const QByteArray& data) { + switch (data.at(0)) { + case NULL_HEIGHTFIELD_IMAGE: + default: + return QImage(); + + case NORMAL_HEIGHTFIELD_IMAGE: + return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1)); + + case DEFLATED_HEIGHTFIELD_IMAGE: { + QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1); + const int OFFSET_SIZE = sizeof(qint32) * 2; + QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE); + const qint32* offsets = (const qint32*)inflated.constData(); + image.setOffset(QPoint(offsets[0], offsets[1])); + return image; + } + } +} + HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color) { if (!reference) { read(in, bytes, color); @@ -502,7 +545,10 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData reference->_encodedDelta = in.readAligned(bytes); reference->_deltaData = this; _contents = reference->_contents; - QImage image = QImage::fromData(reference->_encodedDelta); + QImage image = decodeHeightfieldImage(reference->_encodedDelta); + if (image.isNull()) { + return; + } QPoint offset = image.offset(); image = image.convertToFormat(QImage::Format_RGB888); if (offset.x() == 0) { @@ -550,9 +596,7 @@ void HeightfieldData::write(Bitstream& out, bool color) { *dest++ = *src; } } - QBuffer buffer(&_encoded); - buffer.open(QIODevice::WriteOnly); - image.save(&buffer, "PNG"); + _encoded = encodeHeightfieldImage(image); } out << _encoded.size(); out.writeAligned(_encoded); @@ -588,15 +632,17 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r maxY = qMax(maxY, y); } } - int width = qMax(maxX - minX + 1, 0); - int height = qMax(maxY - minY + 1, 0); - image = QImage(width, height, QImage::Format_RGB888); - src = _contents.constData() + (minY * size + minX) * COLOR_BYTES; - int srcStride = size * COLOR_BYTES; - int destStride = width * COLOR_BYTES; - for (int y = 0; y < height; y++) { - memcpy(image.scanLine(y), src, destStride); - src += srcStride; + if (maxX >= minX) { + int width = maxX - minX + 1; + int height = maxY - minY + 1; + image = QImage(width, height, QImage::Format_RGB888); + src = _contents.constData() + (minY * size + minX) * COLOR_BYTES; + int srcStride = size * COLOR_BYTES; + int destStride = width * COLOR_BYTES; + for (int y = 0; y < height; y++) { + memcpy(image.scanLine(y), src, destStride); + src += srcStride; + } } } else { int size = glm::sqrt((float)_contents.size()); @@ -619,24 +665,24 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r maxY = qMax(maxY, y); } } - int width = qMax(maxX - minX + 1, 0); - int height = qMax(maxY - minY + 1, 0); - image = QImage(width, height, QImage::Format_RGB888); - const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX; - for (int y = 0; y < height; y++) { - uchar* dest = image.scanLine(y); - for (const uchar* src = lineSrc, *end = src + width; src != end; src++) { - *dest++ = *src; - *dest++ = *src; - *dest++ = *src; + if (maxX >= minX) { + int width = qMax(maxX - minX + 1, 0); + int height = qMax(maxY - minY + 1, 0); + image = QImage(width, height, QImage::Format_RGB888); + const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX; + for (int y = 0; y < height; y++) { + uchar* dest = image.scanLine(y); + for (const uchar* src = lineSrc, *end = src + width; src != end; src++) { + *dest++ = *src; + *dest++ = *src; + *dest++ = *src; + } + lineSrc += size; } - lineSrc += size; } } - QBuffer buffer(&reference->_encodedDelta); - buffer.open(QIODevice::WriteOnly); image.setOffset(QPoint(minX + 1, minY + 1)); - image.save(&buffer, "PNG"); + reference->_encodedDelta = encodeHeightfieldImage(image); reference->_deltaData = this; } out << reference->_encodedDelta.size(); @@ -644,7 +690,7 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r } void HeightfieldData::read(Bitstream& in, int bytes, bool color) { - set(QImage::fromData(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color); + set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color); } void HeightfieldData::set(const QImage& image, bool color) {