diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 433c8af23e..db19901334 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1011,7 +1011,7 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf && info.size > _buffer->getScale()) { return DEFAULT_ORDER; } - HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); + HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); if (!height) { return STOP_RECURSION; } @@ -1067,7 +1067,7 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { if (colorSize == 0) { return STOP_RECURSION; } - HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); + HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); if (!color) { return STOP_RECURSION; } @@ -1149,14 +1149,14 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { return DEFAULT_ORDER; } HeightfieldBuffer* buffer = NULL; - HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); + HeightfieldHeightDataPointer 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(); + HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); int colorContentsSize = 0; if (color) { const QByteArray& colorContents = color->getContents(); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 0b1ebabe71..0a6e7e5326 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -957,14 +957,28 @@ void ImportHeightfieldTool::apply() { HeightfieldBuffer* buffer = static_cast(bufferData.data()); MetavoxelData data; data.setSize(scale); - HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getUnextendedHeight())); + + QByteArray height = buffer->getUnextendedHeight(); + HeightfieldHeightDataPointer heightPointer(new HeightfieldHeightData(height)); data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); - if (!buffer->getColor().isEmpty()) { - HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor())); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); + + QByteArray color; + if (buffer->getColor().isEmpty()) { + const int WHITE_VALUE = 0xFF; + color = QByteArray(height.size() * HeightfieldData::COLOR_BYTES, WHITE_VALUE); + } else { + color = buffer->getUnextendedColor(); } + HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( + AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); + + QByteArray texture(height.size(), 0); + HeightfieldDataPointer texturePointer(new HeightfieldData(texture)); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue( + AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer)))); + MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( _translation->getValue() + buffer->getTranslation() * scale, data)) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); @@ -1165,20 +1179,20 @@ QVariant HeightfieldColorBrushTool::createEdit(bool alternate) { HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) : HeightfieldBrushTool(editor, "Texture Brush") { - _form->addRow("URL:", _url = new QUrlEditor(this)); - _url->setURL(QUrl()); - connect(_url, &QUrlEditor::urlChanged, this, &HeightfieldTextureBrushTool::updateTexture); + _form->addRow(_textureEditor = new SharedObjectEditor(&HeightfieldTexture::staticMetaObject, false)); + connect(_textureEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldTextureBrushTool::updateTexture); } QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) { if (alternate) { - return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), QUrl(), QColor())); + return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); } else { - return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), _url->getURL(), + return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), _textureEditor->getObject(), _texture ? _texture->getAverageColor() : QColor())); } } void HeightfieldTextureBrushTool::updateTexture() { - _texture = Application::getInstance()->getTextureCache()->getTexture(_url->getURL()); + HeightfieldTexture* texture = static_cast(_textureEditor->getObject().data()); + _texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL()); } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 63f71c6ca4..7e37b819d7 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -26,9 +26,9 @@ class QListWidget; class QPushButton; class QScrollArea; class QSpinBox; -class QUrlEditor; class MetavoxelTool; +class SharedObjectEditor; class Vec3Editor; /// Allows editing metavoxels. @@ -380,7 +380,7 @@ private slots: private: - QUrlEditor* _url; + SharedObjectEditor* _textureEditor; QSharedPointer _texture; }; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index b1865c258c..1ac04c9f82 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -23,6 +23,7 @@ REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(SpannerQRgbAttribute) REGISTER_META_OBJECT(SpannerPackedNormalAttribute) +REGISTER_META_OBJECT(HeightfieldTexture) REGISTER_META_OBJECT(HeightfieldAttribute) REGISTER_META_OBJECT(HeightfieldColorAttribute) REGISTER_META_OBJECT(HeightfieldTextureAttribute) @@ -502,8 +503,7 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) : _contents(contents) { } -HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { - read(in, bytes, color); +HeightfieldData::~HeightfieldData() { } enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; @@ -549,51 +549,48 @@ const QImage decodeHeightfieldImage(const QByteArray& data) { } } -HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color) { +HeightfieldHeightData::HeightfieldHeightData(const QByteArray& contents) : + HeightfieldData(contents) { +} + +HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference) { if (!reference) { - read(in, bytes, color); + read(in, bytes); return; } - QMutexLocker locker(&reference->_encodedDeltaMutex); - reference->_encodedDelta = in.readAligned(bytes); - reference->_deltaData = this; - _contents = reference->_contents; - QImage image = decodeHeightfieldImage(reference->_encodedDelta); + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(HeightfieldDataPointer(this)); + _contents = reference->getContents(); + QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); if (image.isNull()) { return; } QPoint offset = image.offset(); image = image.convertToFormat(QImage::Format_RGB888); if (offset.x() == 0) { - set(image, color); + set(image); return; } int minX = offset.x() - 1; int minY = offset.y() - 1; - if (color) { - 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; - } - } else { - 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 += COLOR_BYTES) { - *dest = *src; - } - lineDest += size; + 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 += COLOR_BYTES) { + *dest = *src; } + lineDest += size; } } -HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& ancestor, - const glm::vec3& minimum, float size, bool color) { +HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, + const glm::vec3& minimum, float size) { QMutexLocker locker(&_encodedSubdivisionsMutex); int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; if (_encodedSubdivisions.size() <= index) { @@ -610,61 +607,35 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData int destSize = image.width(); const uchar* src = image.constBits(); const QByteArray& ancestorContents = ancestor->getContents(); - if (color) { - int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - int ancestorStride = ancestorSize * COLOR_BYTES; - - _contents = QByteArray(destSize * destSize * COLOR_BYTES, 0); - char* dest = _contents.data(); - int stride = image.width() * COLOR_BYTES; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; - float ancestorX = minimum.x * ancestorSize; - for (char* end = dest + stride; dest != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; - *dest++ = *ref++ + *src++; - *dest++ = *ref++ + *src++; - *dest++ = *ref++ + *src++; - } - } - } else { - int ancestorSize = glm::sqrt((float)ancestorContents.size()); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - - _contents = QByteArray(destSize * destSize, 0); - char* dest = _contents.data(); - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; - float ancestorX = minimum.x * ancestorSize; - for (char* end = dest + destSize; dest != end; src += COLOR_BYTES, ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX; - *dest++ = *ref++ + *src; - } + + int ancestorSize = glm::sqrt((float)ancestorContents.size()); + float ancestorY = minimum.z * ancestorSize; + float ancestorIncrement = size * ancestorSize / destSize; + + _contents = QByteArray(destSize * destSize, 0); + char* dest = _contents.data(); + + for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { + const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; + float ancestorX = minimum.x * ancestorSize; + for (char* end = dest + destSize; dest != end; src += COLOR_BYTES, ancestorX += ancestorIncrement) { + const uchar* ref = lineRef + (int)ancestorX; + *dest++ = *ref++ + *src; } } } -void HeightfieldData::write(Bitstream& out, bool color) { +void HeightfieldHeightData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { QImage image; - if (color) { - 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()); - image = QImage(size, size, QImage::Format_RGB888); - uchar* dest = image.bits(); - for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) { - *dest++ = *src; - *dest++ = *src; - *dest++ = *src; - } + int size = glm::sqrt((float)_contents.size()); + image = QImage(size, size, QImage::Format_RGB888); + uchar* dest = image.bits(); + for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) { + *dest++ = *src; + *dest++ = *src; + *dest++ = *src; } _encoded = encodeHeightfieldImage(image); } @@ -672,95 +643,58 @@ void HeightfieldData::write(Bitstream& out, bool color) { out.writeAligned(_encoded); } -void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color) { +void HeightfieldHeightData::writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference) { if (!reference || reference->getContents().size() != _contents.size()) { - write(out, color); + write(out); return; } - QMutexLocker locker(&reference->_encodedDeltaMutex); - if (reference->_encodedDelta.isEmpty() || reference->_deltaData != this) { + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { QImage image; - int minX, minY; - if (color) { - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - 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 += 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); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); + int size = glm::sqrt((float)_contents.size()); + int minX = size, minY = size; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().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 (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; - } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); } - } 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); - } - } - 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; + } + 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; } } image.setOffset(QPoint(minX + 1, minY + 1)); - reference->_encodedDelta = encodeHeightfieldImage(image); - reference->_deltaData = this; + reference->setEncodedDelta(encodeHeightfieldImage(image)); + reference->setDeltaData(HeightfieldDataPointer(this)); } - out << reference->_encodedDelta.size(); - out.writeAligned(reference->_encodedDelta); + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); } -void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPointer& ancestor, - const glm::vec3& minimum, float size, bool color) { +void HeightfieldHeightData::writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, + const glm::vec3& minimum, float size) { QMutexLocker locker(&_encodedSubdivisionsMutex); int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; if (_encodedSubdivisions.size() <= index) { @@ -771,48 +705,26 @@ void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPoint QImage image; const QByteArray& ancestorContents = ancestor->getContents(); const uchar* src = (const uchar*)_contents.constData(); - if (color) { - int destSize = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - image = QImage(destSize, destSize, QImage::Format_RGB888); - uchar* dest = image.bits(); - int stride = destSize * COLOR_BYTES; - - int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - int ancestorStride = ancestorSize * COLOR_BYTES; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; - float ancestorX = minimum.x * ancestorSize; - for (const uchar* end = src + stride; src != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; - *dest++ = *src++ - *ref++; - *dest++ = *src++ - *ref++; - *dest++ = *src++ - *ref++; - } - } - } else { - int destSize = glm::sqrt((float)_contents.size()); - image = QImage(destSize, destSize, QImage::Format_RGB888); - uchar* dest = image.bits(); - - int ancestorSize = glm::sqrt((float)ancestorContents.size()); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; - float ancestorX = minimum.x * ancestorSize; - for (const uchar* end = src + destSize; src != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX; - uchar difference = *src++ - *ref; - *dest++ = difference; - *dest++ = difference; - *dest++ = difference; - } - } - } + + int destSize = glm::sqrt((float)_contents.size()); + image = QImage(destSize, destSize, QImage::Format_RGB888); + uchar* dest = image.bits(); + + int ancestorSize = glm::sqrt((float)ancestorContents.size()); + float ancestorY = minimum.z * ancestorSize; + float ancestorIncrement = size * ancestorSize / destSize; + + for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { + const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; + float ancestorX = minimum.x * ancestorSize; + for (const uchar* end = src + destSize; src != end; ancestorX += ancestorIncrement) { + const uchar* ref = lineRef + (int)ancestorX; + uchar difference = *src++ - *ref; + *dest++ = difference; + *dest++ = difference; + *dest++ = difference; + } + } subdivision.data = encodeHeightfieldImage(image, true); subdivision.ancestor = ancestor; } @@ -820,27 +732,211 @@ void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPoint out.writeAligned(subdivision.data); } -void HeightfieldData::read(Bitstream& in, int bytes, bool color) { - set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color); +void HeightfieldHeightData::read(Bitstream& in, int bytes) { + set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888)); } -void HeightfieldData::set(const QImage& image, bool color) { - if (color) { - _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() * COLOR_BYTES; - src != end; src += COLOR_BYTES) { - *dest++ = *src; +void HeightfieldHeightData::set(const QImage& image) { + _contents.resize(image.width() * image.height()); + char* dest = _contents.data(); + for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES; + src != end; src += COLOR_BYTES) { + *dest++ = *src; + } +} + +HeightfieldColorData::HeightfieldColorData(const QByteArray& contents) : + HeightfieldData(contents) { +} + +HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(HeightfieldDataPointer(this)); + _contents = reference->getContents(); + QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); + if (image.isNull()) { + return; + } + QPoint offset = image.offset(); + image = image.convertToFormat(QImage::Format_RGB888); + if (offset.x() == 0) { + set(image); + return; + } + int minX = offset.x() - 1; + int minY = offset.y() - 1; + 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; + } +} + +HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, + const glm::vec3& minimum, float size) { + QMutexLocker locker(&_encodedSubdivisionsMutex); + int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; + if (_encodedSubdivisions.size() <= index) { + _encodedSubdivisions.resize(index + 1); + } + EncodedSubdivision& subdivision = _encodedSubdivisions[index]; + subdivision.data = in.readAligned(bytes); + subdivision.ancestor = ancestor; + QImage image = decodeHeightfieldImage(subdivision.data); + if (image.isNull()) { + return; + } + image = image.convertToFormat(QImage::Format_RGB888); + int destSize = image.width(); + const uchar* src = image.constBits(); + const QByteArray& ancestorContents = ancestor->getContents(); + + int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); + float ancestorY = minimum.z * ancestorSize; + float ancestorIncrement = size * ancestorSize / destSize; + int ancestorStride = ancestorSize * COLOR_BYTES; + + _contents = QByteArray(destSize * destSize * COLOR_BYTES, 0); + char* dest = _contents.data(); + int stride = image.width() * COLOR_BYTES; + + for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { + const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; + float ancestorX = minimum.x * ancestorSize; + for (char* end = dest + stride; dest != end; ancestorX += ancestorIncrement) { + const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; + *dest++ = *ref++ + *src++; + *dest++ = *ref++ + *src++; + *dest++ = *ref++ + *src++; } } } +void HeightfieldColorData::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + QImage image; + int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); + image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); + _encoded = encodeHeightfieldImage(image); + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void HeightfieldColorData::writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference) { + if (!reference || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + QImage image; + int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); + int minX = size, minY = size; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().constData(); + for (int y = 0; y < size; y++) { + bool difference = false; + 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); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + 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; + } + } + image.setOffset(QPoint(minX + 1, minY + 1)); + reference->setEncodedDelta(encodeHeightfieldImage(image)); + reference->setDeltaData(HeightfieldDataPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void HeightfieldColorData::writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, + const glm::vec3& minimum, float size) { + QMutexLocker locker(&_encodedSubdivisionsMutex); + int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; + if (_encodedSubdivisions.size() <= index) { + _encodedSubdivisions.resize(index + 1); + } + EncodedSubdivision& subdivision = _encodedSubdivisions[index]; + if (subdivision.data.isEmpty() || subdivision.ancestor != ancestor) { + QImage image; + const QByteArray& ancestorContents = ancestor->getContents(); + const uchar* src = (const uchar*)_contents.constData(); + + int destSize = glm::sqrt(_contents.size() / (float)COLOR_BYTES); + image = QImage(destSize, destSize, QImage::Format_RGB888); + uchar* dest = image.bits(); + int stride = destSize * COLOR_BYTES; + + int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); + float ancestorY = minimum.z * ancestorSize; + float ancestorIncrement = size * ancestorSize / destSize; + int ancestorStride = ancestorSize * COLOR_BYTES; + + for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { + const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; + float ancestorX = minimum.x * ancestorSize; + for (const uchar* end = src + stride; src != end; ancestorX += ancestorIncrement) { + const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; + *dest++ = *src++ - *ref++; + *dest++ = *src++ - *ref++; + *dest++ = *src++ - *ref++; + } + } + subdivision.data = encodeHeightfieldImage(image, true); + subdivision.ancestor = ancestor; + } + out << subdivision.data.size(); + out.writeAligned(subdivision.data); +} + +void HeightfieldColorData::read(Bitstream& in, int bytes) { + set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888)); +} + +void HeightfieldColorData::set(const QImage& image) { + _contents.resize(image.width() * image.height() * COLOR_BYTES); + memcpy(_contents.data(), image.constBits(), _contents.size()); +} + +HeightfieldTexture::HeightfieldTexture() { +} + HeightfieldAttribute::HeightfieldAttribute(const QString& name) : - InlineAttribute(name) { + InlineAttribute(name) { } void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { @@ -850,9 +946,9 @@ void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const int size; in >> size; if (size == 0) { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(); } else { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false)); + *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData(in, size)); } } @@ -860,9 +956,9 @@ void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const if (!isLeaf) { return; } - HeightfieldDataPointer data = decodeInline(value); + HeightfieldHeightDataPointer data = decodeInline(value); if (data) { - data->write(out, false); + data->write(out); } else { out << 0; } @@ -875,10 +971,10 @@ void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* referenc int size; in >> size; if (size == 0) { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(); } else { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData( - in, size, decodeInline(reference), false)); + *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData( + in, size, decodeInline(reference))); } } @@ -886,9 +982,9 @@ void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* referen if (!isLeaf) { return; } - HeightfieldDataPointer data = decodeInline(value); + HeightfieldHeightDataPointer data = decodeInline(value); if (data) { - data->writeDelta(out, decodeInline(reference), false); + data->writeDelta(out, decodeInline(reference)); } else { out << 0; } @@ -897,20 +993,20 @@ void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* referen bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const { int maxSize = 0; for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldDataPointer pointer = decodeInline(children[i]); + HeightfieldHeightDataPointer pointer = decodeInline(children[i]); if (pointer) { maxSize = qMax(maxSize, pointer->getContents().size()); } } if (maxSize == 0) { - *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); + *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer(); return true; } int size = glm::sqrt((float)maxSize); QByteArray contents(size * size, 0); int halfSize = size / 2; for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldDataPointer child = decodeInline(children[i]); + HeightfieldHeightDataPointer child = decodeInline(children[i]); if (!child) { continue; } @@ -920,7 +1016,7 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) int xIndex = i & INDEX_MASK; const int Y_SHIFT = 1; int yIndex = (i >> Y_SHIFT) & INDEX_MASK; - if (yIndex == 0 && decodeInline(children[i | (1 << Y_SHIFT)])) { + if (yIndex == 0 && decodeInline(children[i | (1 << Y_SHIFT)])) { continue; // bottom is overriden by top } const int HALF_RANGE = 128; @@ -959,12 +1055,12 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) } } } - *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents)); + *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer(new HeightfieldHeightData(contents)); return false; } HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : - InlineAttribute(name) { + InlineAttribute(name) { } void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { @@ -974,9 +1070,9 @@ void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) c int size; in >> size; if (size == 0) { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(); } else { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true)); + *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData(in, size)); } } @@ -984,9 +1080,9 @@ void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) if (!isLeaf) { return; } - HeightfieldDataPointer data = decodeInline(value); + HeightfieldColorDataPointer data = decodeInline(value); if (data) { - data->write(out, true); + data->write(out); } else { out << 0; } @@ -999,10 +1095,10 @@ void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* ref int size; in >> size; if (size == 0) { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); + *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(); } else { - *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData( - in, size, decodeInline(reference), true)); + *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData( + in, size, decodeInline(reference))); } } @@ -1010,9 +1106,9 @@ void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* re if (!isLeaf) { return; } - HeightfieldDataPointer data = decodeInline(value); + HeightfieldColorDataPointer data = decodeInline(value); if (data) { - data->writeDelta(out, decodeInline(reference), true); + data->writeDelta(out, decodeInline(reference)); } else { out << 0; } @@ -1021,20 +1117,20 @@ void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* re bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const { int maxSize = 0; for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldDataPointer pointer = decodeInline(children[i]); + HeightfieldColorDataPointer pointer = decodeInline(children[i]); if (pointer) { maxSize = qMax(maxSize, pointer->getContents().size()); } } if (maxSize == 0) { - *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); + *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(); return true; } 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]); + HeightfieldColorDataPointer child = decodeInline(children[i]); if (!child) { continue; } @@ -1044,7 +1140,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post int xIndex = i & INDEX_MASK; const int Y_SHIFT = 1; int yIndex = (i >> Y_SHIFT) & INDEX_MASK; - if (yIndex == 0 && decodeInline(children[i | (1 << Y_SHIFT)])) { + if (yIndex == 0 && decodeInline(children[i | (1 << Y_SHIFT)])) { continue; // bottom is overriden by top } int Z_SHIFT = 2; @@ -1098,7 +1194,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post } } } - *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents)); + *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(new HeightfieldColorData(contents)); return false; } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 85bc5b418c..5daff8bdc7 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "Bitstream.h" @@ -28,7 +29,9 @@ class QScriptEngine; class QScriptValue; class Attribute; +class HeightfieldColorData; class HeightfieldData; +class HeightfieldHeightData; class MetavoxelData; class MetavoxelLOD; class MetavoxelNode; @@ -439,23 +442,20 @@ 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); - HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& ancestor, - const glm::vec3& minimum, float size, bool color); + HeightfieldData(const QByteArray& contents = QByteArray()); + virtual ~HeightfieldData(); const QByteArray& getContents() const { return _contents; } - void write(Bitstream& out, bool color); - void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color); - void writeSubdivided(Bitstream& out, const HeightfieldDataPointer& ancestor, - const glm::vec3& minimum, float size, bool color); - -private: + void setDeltaData(const HeightfieldDataPointer& deltaData) { _deltaData = deltaData; } + const HeightfieldDataPointer& getDeltaData() const { return _deltaData; } - void read(Bitstream& in, int bytes, bool color); - void set(const QImage& image, bool color); + void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; } + const QByteArray& getEncodedDelta() const { return _encodedDelta; } + + QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; } + +protected: QByteArray _contents; QByteArray _encoded; @@ -474,8 +474,70 @@ private: QMutex _encodedSubdivisionsMutex; }; +typedef QExplicitlySharedDataPointer HeightfieldHeightDataPointer; + +/// Contains a block of heightfield height data. +class HeightfieldHeightData : public HeightfieldData { +public: + + HeightfieldHeightData(const QByteArray& contents); + HeightfieldHeightData(Bitstream& in, int bytes); + HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference); + HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, + const glm::vec3& minimum, float size); + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference); + void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, + const glm::vec3& minimum, float size); + +private: + + void read(Bitstream& in, int bytes); + void set(const QImage& image); +}; + +typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; + +/// Contains a block of heightfield color data. +class HeightfieldColorData : public HeightfieldData { +public: + + HeightfieldColorData(const QByteArray& contents); + HeightfieldColorData(Bitstream& in, int bytes); + HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference); + HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, + const glm::vec3& minimum, float size); + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference); + void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, + const glm::vec3& minimum, float size); + +private: + + void read(Bitstream& in, int bytes); + void set(const QImage& image); +}; + +/// Contains the description of a heightfield texture. +class HeightfieldTexture : public SharedObject { + Q_OBJECT + Q_PROPERTY(QUrl url MEMBER _url) + +public: + + Q_INVOKABLE HeightfieldTexture(); + + const QUrl& getURL() const { return _url; } + +private: + + QUrl _url; +}; + /// An attribute that stores heightfield data. -class HeightfieldAttribute : public InlineAttribute { +class HeightfieldAttribute : public InlineAttribute { Q_OBJECT public: @@ -492,7 +554,7 @@ public: }; /// An attribute that stores heightfield colors. -class HeightfieldColorAttribute : public InlineAttribute { +class HeightfieldColorAttribute : public InlineAttribute { Q_OBJECT public: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 34bd6707fd..9ce6e4a6b8 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -354,7 +354,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } - HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue(); + HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue(); if (!pointer) { return STOP_RECURSION; } @@ -396,8 +396,8 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { lineDest += size; } if (changed) { - HeightfieldDataPointer newPointer(new HeightfieldData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } return STOP_RECURSION; } @@ -442,7 +442,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } - HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue(); + HeightfieldColorDataPointer pointer = info.inputValues.at(0).getInlineValue(); if (!pointer) { return STOP_RECURSION; } @@ -481,8 +481,8 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { lineDest += stride; } if (changed) { - HeightfieldDataPointer newPointer(new HeightfieldData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } return STOP_RECURSION; } @@ -493,10 +493,10 @@ void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjec } PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius, - const QUrl& url, const QColor& averageColor) : + const SharedObjectPointer& texture, const QColor& averageColor) : position(position), radius(radius), - url(url), + texture(texture), averageColor(averageColor) { } @@ -531,7 +531,7 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } - HeightfieldDataPointer pointer = info.inputValues.at(1).getInlineValue(); + HeightfieldColorDataPointer pointer = info.inputValues.at(1).getInlineValue(); if (!pointer) { return STOP_RECURSION; } @@ -570,8 +570,8 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { lineDest += stride; } if (changed) { - HeightfieldDataPointer newPointer(new HeightfieldData(contents)); - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); + HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); } return STOP_RECURSION; } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 0455e6790e..3d610b10df 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -249,11 +249,11 @@ public: STREAM glm::vec3 position; STREAM float radius; - STREAM QUrl url; + STREAM SharedObjectPointer texture; STREAM QColor averageColor; - PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const QUrl& url = QUrl(), - const QColor& averageColor = QColor()); + PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, + const SharedObjectPointer& texture = SharedObjectPointer(), const QColor& averageColor = QColor()); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 053ef57bad..c125e1352b 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -158,7 +158,7 @@ SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nulla _type->addItem("(none)"); } foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { - // add add constructable subclasses + // add constructable subclasses if (metaObject->constructorCount() > 0) { _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); } @@ -226,6 +226,7 @@ void SharedObjectEditor::updateType() { const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); if (!metaObject) { _object.reset(); + emit objectChanged(_object); return; } QObject* newObject = metaObject->newInstance(); @@ -259,7 +260,7 @@ void SharedObjectEditor::updateType() { } } } - _object = static_cast(newObject); + emit objectChanged(_object = static_cast(newObject)); } void SharedObjectEditor::propertyChanged() { @@ -275,6 +276,7 @@ void SharedObjectEditor::propertyChanged() { QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); property.write(object, widget->property(valuePropertyName)); } + emit objectChanged(_object); } void SharedObjectEditor::updateProperty() { diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 407fc820c8..157987ed6f 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -211,7 +211,7 @@ Q_DECLARE_METATYPE(SharedObjectSet) /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT - Q_PROPERTY(SharedObjectPointer object READ getObject WRITE setObject USER true) + Q_PROPERTY(SharedObjectPointer object READ getObject WRITE setObject NOTIFY objectChanged USER true) public: @@ -222,6 +222,10 @@ public: /// "Detaches" the object pointer, copying it if anyone else is holding a reference. void detachObject(); +signals: + + void objectChanged(const SharedObjectPointer& object); + public slots: void setObject(const SharedObjectPointer& object);