From f8afdba92216fe46080f2c4b72ec5d37e7d023a5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 4 Aug 2014 16:56:30 -0700 Subject: [PATCH] Allow configurable block sizes, merging children with different resolutions. --- interface/src/ui/MetavoxelEditor.cpp | 32 +++++--- interface/src/ui/MetavoxelEditor.h | 5 +- .../metavoxels/src/AttributeRegistry.cpp | 79 ++++++++++++++----- 3 files changed, 81 insertions(+), 35 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index cf4fd0bba9..096798e77e 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -930,6 +930,13 @@ void HeightfieldTool::render() { ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : HeightfieldTool(editor, "Import Heightfield") { + _form->addRow("Block Size:", _blockSize = new QSpinBox()); + _blockSize->setPrefix("2^"); + _blockSize->setMinimum(1); + _blockSize->setValue(5); + + connect(_blockSize, static_cast(&QSpinBox::valueChanged), this, + &ImportHeightfieldTool::updatePreview); _form->addRow("Height:", _height = new QPushButton()); connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); _form->addRow("Color:", _color = new QPushButton()); @@ -989,23 +996,22 @@ void ImportHeightfieldTool::selectColorFile() { updatePreview(); } -const int BLOCK_SIZE = 32; -const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1; - void ImportHeightfieldTool::updatePreview() { QVector buffers; if (_heightImage.width() > 0 && _heightImage.height() > 0) { float z = 0.0f; - for (int i = 0; i < _heightImage.height(); i += BLOCK_ADVANCEMENT, z++) { + int blockSize = pow(2.0, _blockSize->value()); + int blockAdvancement = blockSize - 1; + for (int i = 0; i < _heightImage.height(); i += blockAdvancement, z++) { float x = 0.0f; - for (int j = 0; j < _heightImage.width(); j += BLOCK_ADVANCEMENT, x++) { - QByteArray height(BLOCK_SIZE * BLOCK_SIZE, 0); - int rows = qMin(BLOCK_SIZE, _heightImage.height() - i); - int columns = qMin(BLOCK_SIZE, _heightImage.width() - j); + 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); 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 * BLOCK_SIZE; + char* dest = height.data() + y * blockSize; for (int x = 0; x < columns; x++) { *dest++ = *src; src += BYTES_PER_COLOR; @@ -1014,11 +1020,11 @@ void ImportHeightfieldTool::updatePreview() { QByteArray color; if (!_colorImage.isNull()) { - color = QByteArray(BLOCK_SIZE * BLOCK_SIZE * BYTES_PER_COLOR, 0); - rows = qMax(0, qMin(BLOCK_SIZE, _colorImage.height() - i)); - columns = qMax(0, qMin(BLOCK_SIZE, _colorImage.width() - j)); + color = QByteArray(blockSize * blockSize * BYTES_PER_COLOR, 0); + rows = qMax(0, qMin(blockSize, _colorImage.height() - i)); + columns = qMax(0, qMin(blockSize, _colorImage.width() - j)); for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR, + memcpy(color.data() + y * blockSize * BYTES_PER_COLOR, _colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR); } } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index abb1450f40..cc30896c49 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -267,10 +267,11 @@ private slots: void selectHeightFile(); void selectColorFile(); - + void updatePreview(); + private: - void updatePreview(); + QSpinBox* _blockSize; QPushButton* _height; QPushButton* _color; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 7219e9da41..40b6195ada 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -559,9 +559,6 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) } const QByteArray& childContents = child->getContents(); int childSize = glm::sqrt((float)childContents.size()); - if (childSize != size) { - continue; // TODO: handle differently-sized children - } const int INDEX_MASK = 1; int xIndex = i & INDEX_MASK; const int Y_SHIFT = 1; @@ -576,13 +573,33 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) char* dest = contents.data() + (zIndex * halfSize * size) + (xIndex * halfSize); uchar* src = (uchar*)childContents.data(); int childSizePlusOne = childSize + 1; - for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize; dest != end; src += 2) { - int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); - *dest++ = (max == 0) ? 0 : (yOffset + (max >> 1)); + if (childSize == size) { + // simple case: one destination value for four child values + for (int z = 0; z < halfSize; z++) { + for (char* end = dest + halfSize; dest != end; src += 2) { + int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); + *dest++ = (max == 0) ? 0 : (yOffset + (max >> 1)); + } + dest += halfSize; + src += childSize; + } + } else { + // more complex: N destination values for four child values + int halfChildSize = childSize / 2; + int destPerSrc = size / childSize; + for (int z = 0; z < halfChildSize; z++) { + for (uchar* end = src + childSize; src != end; src += 2) { + int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); + memset(dest, (max == 0) ? 0 : (yOffset + (max >> 1)), destPerSrc); + dest += destPerSrc; + } + dest += halfSize; + for (int j = 1; j < destPerSrc; j++) { + memcpy(dest, dest - size, halfSize); + dest += size; + } + src += childSize; } - dest += halfSize; - src += childSize; } } *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents)); @@ -638,9 +655,6 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post } const QByteArray& childContents = child->getContents(); int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL); - if (childSize != size) { - continue; // TODO: handle differently-sized children - } const int INDEX_MASK = 1; int xIndex = i & INDEX_MASK; const int Y_SHIFT = 1; @@ -653,7 +667,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL; uchar* src = (uchar*)childContents.data(); int childStride = childSize * BYTES_PER_PIXEL; - int halfStride = halfSize * BYTES_PER_PIXEL; + int stride = size * BYTES_PER_PIXEL; + int halfStride = stride / 2; int childStep = 2 * BYTES_PER_PIXEL; int redOffset3 = childStride + BYTES_PER_PIXEL; int greenOffset1 = BYTES_PER_PIXEL + 1; @@ -662,14 +677,38 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post int blueOffset1 = BYTES_PER_PIXEL + 2; int blueOffset2 = childStride + 2; int blueOffset3 = childStride + BYTES_PER_PIXEL + 2; - 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; - *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; + 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; + *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; + } + dest += halfStride; + src += childStride; + } + } else { + // more complex: N destination values for four child values + 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; + *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; + } + } + dest += halfStride; + for (int j = 1; j < destPerSrc; j++) { + memcpy(dest, dest - stride, halfStride); + dest += stride; + } + src += childStride; } - dest += halfStride; - src += childStride; } } *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));