From a83ff4533555ef3869ec65b63966a15ffe0a3125 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 15 Aug 2014 18:09:58 -0700 Subject: [PATCH 01/15] Working on custom encodings for subdivisions. --- libraries/metavoxels/src/AttributeRegistry.h | 4 ++ libraries/metavoxels/src/MetavoxelData.cpp | 42 +++++++++++++++++++- libraries/metavoxels/src/MetavoxelData.h | 3 ++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index ddf6105662..a67fcd1083 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -221,6 +221,10 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } + virtual void readSubdivision(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } + virtual void writeSubdivision(Bitstream& out, void* value, void* reference, bool isLeaf) const { + write(out, value, isLeaf); } + virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 3607441461..2c7542ef35 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -998,7 +998,7 @@ MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); newNode->_children[i] = new MetavoxelNode(state.base.attribute); - newNode->_children[i]->read(nextState); + newNode->_children[i]->readSubdivided(nextState); } return newNode; } @@ -1037,7 +1037,7 @@ void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); - _children[i]->write(nextState); + _children[i]->writeSubdivided(nextState); } } } else if (!leaf) { @@ -1051,6 +1051,44 @@ void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { } } +void MetavoxelNode::readSubdivided(MetavoxelStreamState& state) { + clearChildren(state.base.attribute); + + if (!state.shouldSubdivide()) { + state.base.attribute->read(state.base.stream, _attributeValue, true); + return; + } + bool leaf; + state.base.stream >> leaf; + state.base.attribute->read(state.base.stream, _attributeValue, leaf); + if (!leaf) { + MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i] = new MetavoxelNode(state.base.attribute); + _children[i]->readSubdivided(nextState); + } + mergeChildren(state.base.attribute, true); + } +} + +void MetavoxelNode::writeSubdivided(MetavoxelStreamState& state) const { + if (!state.shouldSubdivide()) { + state.base.attribute->write(state.base.stream, _attributeValue, true); + return; + } + bool leaf = isLeaf(); + state.base.stream << leaf; + state.base.attribute->write(state.base.stream, _attributeValue, leaf); + if (!leaf) { + MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSubdivided(nextState); + } + } +} + void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const { foreach (const SharedObjectPointer& object, decodeInline(_attributeValue)) { if (static_cast(object.data())->testAndSetVisited(state.base.visit)) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 9e5b2f04d1..d01845eba6 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -225,6 +225,9 @@ public: MetavoxelNode* readSubdivision(MetavoxelStreamState& state); void writeSubdivision(MetavoxelStreamState& state) const; + void readSubdivided(MetavoxelStreamState& state); + void writeSubdivided(MetavoxelStreamState& state) const; + void writeSpanners(MetavoxelStreamState& state) const; void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; void writeSpannerSubdivision(MetavoxelStreamState& state) const; From c2a5c33b7b5411c8faea25b20abb66ed2f8a8049 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 18 Aug 2014 16:07:15 -0700 Subject: [PATCH 02/15] Added ability to encode subdivisions relative to ancestor (not currently using for heightfields, since absolute lossy encoding is more efficient). --- .../metavoxels/src/AttributeRegistry.cpp | 132 +++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 19 ++- libraries/metavoxels/src/MetavoxelData.cpp | 22 +-- libraries/metavoxels/src/MetavoxelData.h | 4 +- 4 files changed, 160 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 1e30aee576..5ffa593004 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -204,6 +204,16 @@ Attribute::Attribute(const QString& name) : Attribute::~Attribute() { } +void Attribute::readSubdivided(MetavoxelStreamState& state, void*& value, + const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const { + read(state.base.stream, value, isLeaf); +} + +void Attribute::writeSubdivided(MetavoxelStreamState& state, void* value, + const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const { + write(state.base.stream, value, isLeaf); +} + MetavoxelNode* Attribute::createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const { return new MetavoxelNode(value); } @@ -495,14 +505,14 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; -static QByteArray encodeHeightfieldImage(const QImage& image) { +static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = false) { 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) { + if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD && !lossless) { qint32 offsetX = image.offset().x(), offsetY = image.offset().y(); buffer.write((char*)&offsetX, sizeof(qint32)); buffer.write((char*)&offsetY, sizeof(qint32)); @@ -579,6 +589,63 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData } } +HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& ancestor, + const glm::vec3& minimum, float size, bool color) { + 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(); + 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; + } + } + } +} + void HeightfieldData::write(Bitstream& out, bool color) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { @@ -689,6 +756,67 @@ void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& r out.writeAligned(reference->_encodedDelta); } +void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPointer& ancestor, + const glm::vec3& minimum, float size, bool color) { + 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(); + 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; + } + } + } + subdivision.data = encodeHeightfieldImage(image, true); + subdivision.ancestor = ancestor; + } + out << subdivision.data.size(); + out.writeAligned(subdivision.data); +} + void HeightfieldData::read(Bitstream& in, int bytes, bool color) { set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index a67fcd1083..c103855d2c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -221,9 +221,10 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } - virtual void readSubdivision(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } - virtual void writeSubdivision(Bitstream& out, void* value, void* reference, bool isLeaf) const { - write(out, value, isLeaf); } + virtual void readSubdivided(MetavoxelStreamState& state, void*& value, + const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const; + virtual void writeSubdivided(MetavoxelStreamState& state, void* value, + const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const; virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; @@ -437,11 +438,15 @@ public: 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); 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: @@ -455,6 +460,14 @@ private: HeightfieldDataPointer _deltaData; QByteArray _encodedDelta; QMutex _encodedDeltaMutex; + + class EncodedSubdivision { + public: + HeightfieldDataPointer ancestor; + QByteArray data; + }; + QVector _encodedSubdivisions; + QMutex _encodedSubdivisionsMutex; }; /// An attribute that stores heightfield data. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2c7542ef35..67fafe1633 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -998,7 +998,7 @@ MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); newNode->_children[i] = new MetavoxelNode(state.base.attribute); - newNode->_children[i]->readSubdivided(nextState); + newNode->_children[i]->readSubdivided(nextState, state, _attributeValue); } return newNode; } @@ -1037,7 +1037,7 @@ void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); - _children[i]->writeSubdivided(nextState); + _children[i]->writeSubdivided(nextState, state, _attributeValue); } } } else if (!leaf) { @@ -1051,40 +1051,42 @@ void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { } } -void MetavoxelNode::readSubdivided(MetavoxelStreamState& state) { +void MetavoxelNode::readSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, + void* ancestorValue) { clearChildren(state.base.attribute); if (!state.shouldSubdivide()) { - state.base.attribute->read(state.base.stream, _attributeValue, true); + state.base.attribute->readSubdivided(state, _attributeValue, ancestorState, ancestorValue, true); return; } bool leaf; state.base.stream >> leaf; - state.base.attribute->read(state.base.stream, _attributeValue, leaf); + state.base.attribute->readSubdivided(state, _attributeValue, ancestorState, ancestorValue, leaf); if (!leaf) { MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); _children[i] = new MetavoxelNode(state.base.attribute); - _children[i]->readSubdivided(nextState); + _children[i]->readSubdivided(nextState, ancestorState, ancestorValue); } mergeChildren(state.base.attribute, true); } } -void MetavoxelNode::writeSubdivided(MetavoxelStreamState& state) const { +void MetavoxelNode::writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, + void* ancestorValue) const { if (!state.shouldSubdivide()) { - state.base.attribute->write(state.base.stream, _attributeValue, true); + state.base.attribute->writeSubdivided(state, _attributeValue, ancestorState, ancestorValue, true); return; } bool leaf = isLeaf(); state.base.stream << leaf; - state.base.attribute->write(state.base.stream, _attributeValue, leaf); + state.base.attribute->writeSubdivided(state, _attributeValue, ancestorState, ancestorValue, leaf); if (!leaf) { MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); - _children[i]->writeSubdivided(nextState); + _children[i]->writeSubdivided(nextState, ancestorState, ancestorValue); } } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index d01845eba6..8308c3c69b 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -225,8 +225,8 @@ public: MetavoxelNode* readSubdivision(MetavoxelStreamState& state); void writeSubdivision(MetavoxelStreamState& state) const; - void readSubdivided(MetavoxelStreamState& state); - void writeSubdivided(MetavoxelStreamState& state) const; + void readSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue); + void writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue) const; void writeSpanners(MetavoxelStreamState& state) const; void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; From 5b8ff074494aab14f0b1432722d8a70b460cd2c6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 18 Aug 2014 18:21:48 -0700 Subject: [PATCH 03/15] Working on heightfield textures. --- interface/src/renderer/TextureCache.cpp | 31 +++++-- interface/src/renderer/TextureCache.h | 6 +- interface/src/ui/MetavoxelEditor.cpp | 25 +++++- interface/src/ui/MetavoxelEditor.h | 23 +++++ .../metavoxels/src/AttributeRegistry.cpp | 40 ++++++++- libraries/metavoxels/src/AttributeRegistry.h | 25 +++++- .../metavoxels/src/MetavoxelMessages.cpp | 88 +++++++++++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 19 ++++ 8 files changed, 245 insertions(+), 12 deletions(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 01c3dc1cc1..11c4778c19 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -382,12 +382,24 @@ void ImageReader::run() { qDebug() << "Image greater than maximum size:" << _url << image.width() << image.height(); image = image.scaled(MAXIMUM_SIZE, MAXIMUM_SIZE, Qt::KeepAspectRatio); } + int imageArea = image.width() * image.height(); + const int EIGHT_BIT_MAXIMUM = 255; if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } - QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false)); + int redTotal = 0, greenTotal = 0, blueTotal = 0; + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + QRgb rgb = image.pixel(x, y); + redTotal += qRed(rgb); + greenTotal += qGreen(rgb); + blueTotal += qBlue(rgb); + } + } + QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false), + Q_ARG(const QColor&, QColor(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea))); return; } if (image.format() != QImage::Format_ARGB32) { @@ -397,11 +409,15 @@ void ImageReader::run() { // check for translucency/false transparency int opaquePixels = 0; int translucentPixels = 0; - const int EIGHT_BIT_MAXIMUM = 255; - const int RGB_BITS = 24; + int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { - int alpha = image.pixel(x, y) >> RGB_BITS; + QRgb rgb = image.pixel(x, y); + redTotal += qRed(rgb); + greenTotal += qGreen(rgb); + blueTotal += qBlue(rgb); + int alpha = qAlpha(rgb); + alphaTotal += alpha; if (alpha == EIGHT_BIT_MAXIMUM) { opaquePixels++; } else if (alpha != 0) { @@ -409,13 +425,13 @@ void ImageReader::run() { } } } - int imageArea = image.width() * image.height(); if (opaquePixels == imageArea) { qDebug() << "Image with alpha channel is completely opaque:" << _url; image = image.convertToFormat(QImage::Format_RGB888); } QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), - Q_ARG(bool, translucentPixels >= imageArea / 2)); + Q_ARG(bool, translucentPixels >= imageArea / 2), Q_ARG(const QColor&, QColor(redTotal / imageArea, + greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea))); } void NetworkTexture::downloadFinished(QNetworkReply* reply) { @@ -427,8 +443,9 @@ void NetworkTexture::loadContent(const QByteArray& content) { QThreadPool::globalInstance()->start(new ImageReader(_self, NULL, _url, content)); } -void NetworkTexture::setImage(const QImage& image, bool translucent) { +void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor) { _translucent = translucent; + _averageColor = averageColor; finishedLoading(true); imageLoaded(image); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 248a451e3a..58f9345c4a 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -127,18 +127,22 @@ public: /// (majority of pixels neither fully opaque or fully transparent). bool isTranslucent() const { return _translucent; } + /// Returns the lazily-computed average texture color. + const QColor& getAverageColor() const { return _averageColor; } + protected: virtual void downloadFinished(QNetworkReply* reply); Q_INVOKABLE void loadContent(const QByteArray& content); - Q_INVOKABLE void setImage(const QImage& image, bool translucent); + Q_INVOKABLE void setImage(const QImage& image, bool translucent, const QColor& averageColor); virtual void imageLoaded(const QImage& image); private: bool _translucent; + QColor _averageColor; }; /// Caches derived, dilated textures. diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b7057532fb..1873f92df8 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -120,6 +120,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new EraseHeightfieldTool(this)); addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldColorBrushTool(this)); + addTool(new HeightfieldTextureBrushTool(this)); updateAttributes(); @@ -1153,5 +1154,27 @@ HeightfieldColorBrushTool::HeightfieldColorBrushTool(MetavoxelEditor* editor) : } QVariant HeightfieldColorBrushTool::createEdit(bool alternate) { - return QVariant::fromValue(PaintHeightfieldColorEdit(_position, _radius->value(), _color->getColor())); + return QVariant::fromValue(PaintHeightfieldColorEdit(_position, _radius->value(), + alternate ? QColor() : _color->getColor())); +} + +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); +} + +QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) { + if (alternate) { + return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), QUrl(), QColor())); + } else { + return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), _url->getURL(), + _texture ? _texture->getAverageColor() : QColor())); + } +} + +void HeightfieldTextureBrushTool::updateTexture() { + _texture = Application::getInstance()->getTextureCache()->getTexture(_url->getURL()); } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 87d95a6927..6413c51e1c 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -26,6 +26,7 @@ class QListWidget; class QPushButton; class QScrollArea; class QSpinBox; +class QUrlEditor; class MetavoxelTool; class Vec3Editor; @@ -359,4 +360,26 @@ private: QColorEditor* _color; }; +/// Allows texturing parts of the heightfield. +class HeightfieldTextureBrushTool : public HeightfieldBrushTool { + Q_OBJECT + +public: + + HeightfieldTextureBrushTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(bool alternate); + +private slots: + + void updateTexture(); + +private: + + QUrlEditor* _url; + QSharedPointer _texture; +}; + #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 5ffa593004..b1865c258c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -25,6 +25,7 @@ REGISTER_META_OBJECT(SpannerQRgbAttribute) REGISTER_META_OBJECT(SpannerPackedNormalAttribute) REGISTER_META_OBJECT(HeightfieldAttribute) REGISTER_META_OBJECT(HeightfieldColorAttribute) +REGISTER_META_OBJECT(HeightfieldTextureAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -49,7 +50,8 @@ AttributeRegistry::AttributeRegistry() : _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))), _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))), _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), - _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))) { + _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), + _heightfieldTextureAttribute(registerAttribute(new HeightfieldTextureAttribute("heightfieldTexture"))) { // our baseline LOD threshold is for voxels; spanners and heightfields are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -58,6 +60,7 @@ AttributeRegistry::AttributeRegistry() : const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); + _heightfieldTextureAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -1099,6 +1102,41 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post return false; } +HeightfieldTextureAttribute::HeightfieldTextureAttribute(const QString& name) : + InlineAttribute(name) { +} + +void HeightfieldTextureAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + +} + +void HeightfieldTextureAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + +} + +void HeightfieldTextureAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { + +} + +void HeightfieldTextureAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { + +} + +bool HeightfieldTextureAttribute::merge(void*& parent, void* children[], bool postRead) const { + int maxSize = 0; + for (int i = 0; i < MERGE_COUNT; i++) { + HeightfieldDataPointer pointer = decodeInline(children[i]); + if (pointer) { + maxSize = qMax(maxSize, pointer->getContents().size()); + } + } + if (maxSize == 0) { + *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); + return true; + } + return false; +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index c103855d2c..85bc5b418c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -96,12 +96,15 @@ public: /// Returns a reference to the standard "spannerMask" attribute. const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } - /// Returns a reference to the standard HeightfieldPointer "heightfield" attribute. + /// Returns a reference to the standard HeightfieldDataPointer "heightfield" attribute. const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; } - /// Returns a reference to the standard HeightfieldColorPointer "heightfieldColor" attribute. + /// Returns a reference to the standard HeightfieldDataPointer "heightfieldColor" attribute. const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; } + /// Returns a reference to the standard HeightfieldDataPointer "heightfieldTexture" attribute. + const AttributePointer& getHeightfieldTextureAttribute() const { return _heightfieldTextureAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -119,6 +122,7 @@ private: AttributePointer _spannerMaskAttribute; AttributePointer _heightfieldAttribute; AttributePointer _heightfieldColorAttribute; + AttributePointer _heightfieldTextureAttribute; }; /// Converts a value to a void pointer. @@ -504,6 +508,23 @@ public: virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; +/// An attribute that stores heightfield textures. +class HeightfieldTextureAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE HeightfieldTextureAttribute(const QString& name = QString()); + + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index df6e8172e4..34bd6707fd 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -492,3 +492,91 @@ void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjec data.guide(visitor); } +PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius, + const QUrl& url, const QColor& averageColor) : + position(position), + radius(radius), + url(url), + averageColor(averageColor) { +} + +class PaintHeightfieldTextureEditVisitor : public MetavoxelVisitor { +public: + + PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit); + + virtual int visit(MetavoxelInfo& info); + +private: + + PaintHeightfieldTextureEdit _edit; + Box _bounds; +}; + +PaintHeightfieldTextureEditVisitor::PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() << + AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector() << + AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() << + AttributeRegistry::getInstance()->getHeightfieldColorAttribute()), + _edit(edit) { + + glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius); + _bounds = Box(_edit.position - extents, _edit.position + extents); +} + +int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + HeightfieldDataPointer pointer = info.inputValues.at(1).getInlineValue(); + if (!pointer) { + return STOP_RECURSION; + } + QByteArray contents(pointer->getContents()); + const int BYTES_PER_PIXEL = 3; + int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL); + int highest = size - 1; + float heightScale = size / info.size; + + glm::vec3 center = (_edit.position - info.minimum) * heightScale; + float scaledRadius = _edit.radius * heightScale; + glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); + + glm::vec3 start = glm::floor(center - extents); + glm::vec3 end = glm::ceil(center + extents); + + // paint all points within the radius + float z = qMax(start.z, 0.0f); + float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); + int stride = size * BYTES_PER_PIXEL; + char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL; + float squaredRadius = scaledRadius * scaledRadius; + char red = _edit.averageColor.red(), green = _edit.averageColor.green(), blue = _edit.averageColor.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) { + float dx = x - center.x, dz = z - center.z; + if (dx * dx + dz * dz <= squaredRadius) { + dest[0] = red; + dest[1] = green; + dest[2] = blue; + changed = true; + } + } + lineDest += stride; + } + if (changed) { + HeightfieldDataPointer newPointer(new HeightfieldData(contents)); + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); + } + return STOP_RECURSION; +} + +void PaintHeightfieldTextureEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + PaintHeightfieldTextureEditVisitor visitor(*this); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 2fc8cbf030..0455e6790e 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -241,4 +241,23 @@ public: DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit) +/// An edit that sets a region of a heightfield texture. +class PaintHeightfieldTextureEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM glm::vec3 position; + STREAM float radius; + STREAM QUrl url; + STREAM QColor averageColor; + + PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const QUrl& url = QUrl(), + const QColor& averageColor = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(PaintHeightfieldTextureEdit) + #endif // hifi_MetavoxelMessages_h From 23a4c21ce11f9849a42a58ec0921dbb8bdc92cf6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 18 Aug 2014 18:29:19 -0700 Subject: [PATCH 04/15] Only allow heightfield brushes on heightfield attributes. --- interface/src/ui/MetavoxelEditor.cpp | 4 ++++ interface/src/ui/MetavoxelEditor.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 1873f92df8..0b1ebabe71 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1101,6 +1101,10 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin _radius->setValue(1.0); } +bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("HeightfieldAttribute"); +} + void HeightfieldBrushTool::render() { if (Application::getInstance()->isMouseHidden()) { return; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 6413c51e1c..63f71c6ca4 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -312,6 +312,8 @@ public: HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name); + virtual bool appliesTo(const AttributePointer& attribute) const; + virtual void render(); virtual bool eventFilter(QObject* watched, QEvent* event); From e854656a7f32d6e3f64e1d094d0fba716a8c30a7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 19 Aug 2014 14:20:47 -0700 Subject: [PATCH 05/15] Rather than using a boolean to indicate "colorness," use a different class. More texture bits. --- interface/src/MetavoxelSystem.cpp | 8 +- interface/src/ui/MetavoxelEditor.cpp | 36 +- interface/src/ui/MetavoxelEditor.h | 4 +- .../metavoxels/src/AttributeRegistry.cpp | 572 ++++++++++-------- libraries/metavoxels/src/AttributeRegistry.h | 92 ++- .../metavoxels/src/MetavoxelMessages.cpp | 22 +- libraries/metavoxels/src/MetavoxelMessages.h | 6 +- libraries/metavoxels/src/SharedObject.cpp | 6 +- libraries/metavoxels/src/SharedObject.h | 6 +- 9 files changed, 465 insertions(+), 287 deletions(-) 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); From 3136e4df76053106c495c9d14cdb85562ec05ab4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 19 Aug 2014 15:28:15 -0700 Subject: [PATCH 06/15] More texture progress. --- interface/src/ui/MetavoxelEditor.cpp | 2 +- .../metavoxels/src/AttributeRegistry.cpp | 151 +++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 22 ++- .../metavoxels/src/MetavoxelMessages.cpp | 62 +++---- 4 files changed, 197 insertions(+), 40 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 0a6e7e5326..6251448dce 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -975,7 +975,7 @@ void ImportHeightfieldTool::apply() { AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); QByteArray texture(height.size(), 0); - HeightfieldDataPointer texturePointer(new HeightfieldData(texture)); + HeightfieldTextureDataPointer texturePointer(new HeightfieldTextureData(texture)); data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer)))); diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 1ac04c9f82..0e56112957 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -932,6 +932,108 @@ void HeightfieldColorData::set(const QImage& image) { memcpy(_contents.data(), image.constBits(), _contents.size()); } +HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents) : + HeightfieldData(contents) { +} + +HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& 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(); + if (offset.x() == 0) { + set(image); + return; + } + int minX = offset.x() - 1; + int minY = offset.y() - 1; + int size = glm::sqrt((float)_contents.size()); + char* dest = _contents.data() + minY * size + minX; + for (int y = 0; y < image.height(); y++) { + memcpy(dest, image.constScanLine(y), image.width()); + dest += size; + } +} + +void HeightfieldTextureData::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + QImage image; + int size = glm::sqrt((float)_contents.size()); + image = QImage((uchar*)_contents.data(), size, size, QImage::Format_Indexed8); + _encoded = encodeHeightfieldImage(image, true); + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& 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((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 (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_Indexed8); + src = _contents.constData() + minY * size + minX; + for (int y = 0; y < height; y++) { + memcpy(image.scanLine(y), src, width); + src += size; + } + } + image.setOffset(QPoint(minX + 1, minY + 1)); + reference->setEncodedDelta(encodeHeightfieldImage(image, true)); + reference->setDeltaData(HeightfieldDataPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void HeightfieldTextureData::read(Bitstream& in, int bytes) { + set(decodeHeightfieldImage(_encoded = in.readAligned(bytes))); +} + +void HeightfieldTextureData::set(const QImage& image) { + _contents.resize(image.width() * image.height()); + memcpy(_contents.data(), image.constBits(), _contents.size()); +} + HeightfieldTexture::HeightfieldTexture() { } @@ -1199,35 +1301,70 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post } HeightfieldTextureAttribute::HeightfieldTextureAttribute(const QString& name) : - InlineAttribute(name) { + InlineAttribute(name) { } void HeightfieldTextureAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(); + } else { + *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(new HeightfieldTextureData(in, size)); + } } void HeightfieldTextureAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - + if (!isLeaf) { + return; + } + HeightfieldTextureDataPointer data = decodeInline(value); + if (data) { + data->write(out); + } else { + out << 0; + } } void HeightfieldTextureAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(); + } else { + *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(new HeightfieldTextureData( + in, size, decodeInline(reference))); + } } void HeightfieldTextureAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - + if (!isLeaf) { + return; + } + HeightfieldTextureDataPointer data = decodeInline(value); + if (data) { + data->writeDelta(out, decodeInline(reference)); + } else { + out << 0; + } } bool HeightfieldTextureAttribute::merge(void*& parent, void* children[], bool postRead) const { int maxSize = 0; for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldDataPointer pointer = decodeInline(children[i]); + HeightfieldTextureDataPointer pointer = decodeInline(children[i]); if (pointer) { maxSize = qMax(maxSize, pointer->getContents().size()); } } if (maxSize == 0) { - *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); + *(HeightfieldTextureDataPointer*)&parent = HeightfieldTextureDataPointer(); return true; } return false; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 5daff8bdc7..cceda9aa5d 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -32,6 +32,7 @@ class Attribute; class HeightfieldColorData; class HeightfieldData; class HeightfieldHeightData; +class HeightfieldTextureData; class MetavoxelData; class MetavoxelLOD; class MetavoxelNode; @@ -520,6 +521,25 @@ private: void set(const QImage& image); }; +typedef QExplicitlySharedDataPointer HeightfieldTextureDataPointer; + +/// Contains a block of heightfield texture data. +class HeightfieldTextureData : public HeightfieldData { +public: + + HeightfieldTextureData(const QByteArray& contents); + HeightfieldTextureData(Bitstream& in, int bytes); + HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference); + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference); + +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 @@ -571,7 +591,7 @@ public: }; /// An attribute that stores heightfield textures. -class HeightfieldTextureAttribute : public InlineAttribute { +class HeightfieldTextureAttribute : public InlineAttribute { Q_OBJECT public: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 9ce6e4a6b8..2af19023f1 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -435,25 +435,18 @@ PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHe _bounds = Box(_edit.position - extents, _edit.position + extents); } -int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldColorDataPointer pointer = info.inputValues.at(0).getInlineValue(); +static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position, float radius, const QColor& color) { + HeightfieldColorDataPointer pointer = info.inputValues.at(index).getInlineValue(); if (!pointer) { - return STOP_RECURSION; + return; } QByteArray contents(pointer->getContents()); - const int BYTES_PER_PIXEL = 3; - int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL); + int size = glm::sqrt((float)contents.size() / HeightfieldData::COLOR_BYTES); int highest = size - 1; float heightScale = size / info.size; - glm::vec3 center = (_edit.position - info.minimum) * heightScale; - float scaledRadius = _edit.radius * heightScale; + glm::vec3 center = (position - info.minimum) * heightScale; + float scaledRadius = radius * heightScale; glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); glm::vec3 start = glm::floor(center - extents); @@ -462,14 +455,14 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { // paint all points within the radius float z = qMax(start.z, 0.0f); float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - int stride = size * BYTES_PER_PIXEL; - char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL; + int stride = size * HeightfieldData::COLOR_BYTES; + char* lineDest = contents.data() + (int)z * stride + (int)startX * HeightfieldData::COLOR_BYTES; float squaredRadius = scaledRadius * scaledRadius; - char red = _edit.color.red(), green = _edit.color.green(), blue = _edit.color.blue(); + char red = color.red(), green = color.green(), blue = color.blue(); bool changed = false; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { char* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest += BYTES_PER_PIXEL) { + for (float x = startX; x <= endX; x += 1.0f, dest += HeightfieldData::COLOR_BYTES) { float dx = x - center.x, dz = z - center.z; if (dx * dx + dz * dz <= squaredRadius) { dest[0] = red; @@ -482,8 +475,19 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { } if (changed) { HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + info.outputValues[index] = AttributeValue(info.inputValues.at(index).getAttribute(), + encodeInline(newPointer)); } +} + +int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + paintColor(info, 0, _edit.position, _edit.radius, _edit.color); return STOP_RECURSION; } @@ -531,13 +535,12 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } - HeightfieldColorDataPointer pointer = info.inputValues.at(1).getInlineValue(); + HeightfieldTextureDataPointer pointer = info.inputValues.at(0).getInlineValue(); if (!pointer) { return STOP_RECURSION; } QByteArray contents(pointer->getContents()); - const int BYTES_PER_PIXEL = 3; - int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL); + int size = glm::sqrt((float)contents.size()); int highest = size - 1; float heightScale = size / info.size; @@ -551,28 +554,25 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { // paint all points within the radius float z = qMax(start.z, 0.0f); float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - int stride = size * BYTES_PER_PIXEL; - char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL; + char* lineDest = contents.data() + (int)z * size + (int)startX; float squaredRadius = scaledRadius * scaledRadius; - char red = _edit.averageColor.red(), green = _edit.averageColor.green(), blue = _edit.averageColor.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) { + for (float x = startX; x <= endX; x += 1.0f, dest++) { float dx = x - center.x, dz = z - center.z; if (dx * dx + dz * dz <= squaredRadius) { - dest[0] = red; - dest[1] = green; - dest[2] = blue; + *dest = 1; changed = true; } } - lineDest += stride; + lineDest += size; } if (changed) { - HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); + HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } + paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor); return STOP_RECURSION; } From 540d07427038c47c7a85cad08e639e130d704edc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 19 Aug 2014 17:20:14 -0700 Subject: [PATCH 07/15] More texture bits. --- .../metavoxels/src/AttributeRegistry.cpp | 16 ++-- libraries/metavoxels/src/AttributeRegistry.h | 7 +- .../metavoxels/src/MetavoxelMessages.cpp | 74 +++++++++++++++++-- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 0e56112957..c09848c201 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -932,8 +932,9 @@ void HeightfieldColorData::set(const QImage& image) { memcpy(_contents.data(), image.constBits(), _contents.size()); } -HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents) : - HeightfieldData(contents) { +HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector& textures) : + HeightfieldData(contents), + _textures(textures) { } HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes) { @@ -947,6 +948,7 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H } QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); + in.readDelta(_textures, reference->getTextures()); reference->setDeltaData(HeightfieldDataPointer(this)); _contents = reference->getContents(); QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); @@ -978,6 +980,7 @@ void HeightfieldTextureData::write(Bitstream& out) { } out << _encoded.size(); out.writeAligned(_encoded); + out << _textures; } void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference) { @@ -1023,10 +1026,12 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); + out.writeDelta(_textures, reference->getTextures()); } void HeightfieldTextureData::read(Bitstream& in, int bytes) { set(decodeHeightfieldImage(_encoded = in.readAligned(bytes))); + in >> _textures; } void HeightfieldTextureData::set(const QImage& image) { @@ -1363,11 +1368,8 @@ bool HeightfieldTextureAttribute::merge(void*& parent, void* children[], bool po maxSize = qMax(maxSize, pointer->getContents().size()); } } - if (maxSize == 0) { - *(HeightfieldTextureDataPointer*)&parent = HeightfieldTextureDataPointer(); - return true; - } - return false; + *(HeightfieldTextureDataPointer*)&parent = HeightfieldTextureDataPointer(); + return maxSize == 0; } SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index cceda9aa5d..f3e6dc0b97 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -527,10 +527,13 @@ typedef QExplicitlySharedDataPointer HeightfieldTextureD class HeightfieldTextureData : public HeightfieldData { public: - HeightfieldTextureData(const QByteArray& contents); + HeightfieldTextureData(const QByteArray& contents, + const QVector& textures = QVector()); HeightfieldTextureData(Bitstream& in, int bytes); HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference); + const QVector& getTextures() const { return _textures; } + void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference); @@ -538,6 +541,8 @@ private: void read(Bitstream& in, int bytes); void set(const QImage& image); + + QVector _textures; }; /// Contains the description of a heightfield texture. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 2af19023f1..33aea9fcb1 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -347,6 +347,8 @@ PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const Paint _bounds = Box(_edit.position - extents, _edit.position + extents); } +const int EIGHT_BIT_MAXIMUM = 255; + int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; @@ -375,8 +377,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; float squaredRadius = scaledRadius * scaledRadius; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - const int EIGHT_BIT_MAXIMUM = 255; + float squaredRadiusReciprocal = 1.0f / squaredRadius; float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size; bool changed = false; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { @@ -528,6 +529,16 @@ PaintHeightfieldTextureEditVisitor::PaintHeightfieldTextureEditVisitor(const Pai _bounds = Box(_edit.position - extents, _edit.position + extents); } +static QHash countIndices(const QByteArray& contents) { + QHash counts; + for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { + if (*src != 0) { + counts[*src]++; + } + } + return counts; +} + int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; @@ -539,7 +550,47 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { if (!pointer) { return STOP_RECURSION; } + QVector textures = pointer->getTextures(); QByteArray contents(pointer->getContents()); + uchar textureIndex = 0; + if (_edit.texture && static_cast(_edit.texture.data())->getURL().isValid()) { + // first look for a matching existing texture, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < textures.size(); i++) { + const SharedObjectPointer& texture = textures.at(i); + if (texture) { + if (texture->equals(_edit.texture.data())) { + textureIndex = i + 1; + break; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (textureIndex == 0) { + if (firstEmptyIndex != -1) { + textures[firstEmptyIndex] = _edit.texture; + textureIndex = firstEmptyIndex + 1; + + } else if (textures.size() < EIGHT_BIT_MAXIMUM) { + textures.append(_edit.texture); + textureIndex = textures.size(); + + } else { + // last resort: find the least-used texture and remove it + QHash counts = countIndices(contents); + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + textureIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)textureIndex, (char)0); + } + } + } int size = glm::sqrt((float)contents.size()); int highest = size - 1; float heightScale = size / info.size; @@ -554,22 +605,33 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { // paint all points within the radius float z = qMax(start.z, 0.0f); float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - char* lineDest = contents.data() + (int)z * size + (int)startX; + uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; float squaredRadius = scaledRadius * scaledRadius; bool changed = false; + QHash counts; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - char* dest = lineDest; + uchar* dest = lineDest; for (float x = startX; x <= endX; x += 1.0f, dest++) { float dx = x - center.x, dz = z - center.z; if (dx * dx + dz * dz <= squaredRadius) { - *dest = 1; + *dest = textureIndex; changed = true; } } lineDest += size; } if (changed) { - HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents)); + // clear any unused textures + QHash counts = countIndices(contents); + for (int i = 0; i < textures.size(); i++) { + if (counts.value(i + 1) == 0) { + textures[i] = SharedObjectPointer(); + } + } + while (!(textures.isEmpty() || textures.last())) { + textures.removeLast(); + } + HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents, textures)); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor); From 7cf7f188f8a2cf33243dec774af034931098f11e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 19 Aug 2014 18:55:18 -0700 Subject: [PATCH 08/15] Yet more texture bits. --- interface/src/MetavoxelSystem.cpp | 99 ++++++++++++++++++++++++---- interface/src/MetavoxelSystem.h | 19 +++++- interface/src/ui/MetavoxelEditor.cpp | 3 +- 3 files changed, 106 insertions(+), 15 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index db19901334..4cdd2498e6 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -449,9 +449,10 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glDepthFunc(GL_LESS); } -void MetavoxelSystem::deleteTextures(int heightID, int colorID) { +void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { glDeleteTextures(1, (GLuint*)&heightID); glDeleteTextures(1, (GLuint*)&colorID); + glDeleteTextures(1, (GLuint*)&textureID); } MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { @@ -601,19 +602,24 @@ 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) : + const QByteArray& height, const QByteArray& color, const QByteArray& texture) : _translation(translation), _scale(scale), _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), _colorBounds(_heightBounds), + _textureBounds(_heightBounds), _height(height), _color(color), + _texture(texture), _heightTextureID(0), _colorTextureID(0), + _textureTextureID(0), _heightSize(glm::sqrt(height.size())), _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), _colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)), - _colorIncrement(scale / (_colorSize - SHARED_EDGE)) { + _colorIncrement(scale / (_colorSize - SHARED_EDGE)), + _textureSize(glm::sqrt(texture.size())), + _textureIncrement(scale / (_textureSize - SHARED_EDGE)) { _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; @@ -622,16 +628,20 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; + + _textureBounds.maximum.x += _textureIncrement * SHARED_EDGE; + _textureBounds.maximum.z += _textureIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { // the textures have to be deleted on the main thread (for its opengl context) if (QThread::currentThread() != Application::getInstance()->thread()) { QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteTextures", - Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID)); + Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID), Q_ARG(int, _textureTextureID)); } else { glDeleteTextures(1, &_heightTextureID); glDeleteTextures(1, &_colorTextureID); + glDeleteTextures(1, &_textureTextureID); } } @@ -692,6 +702,17 @@ void HeightfieldBuffer::render(bool cursor) { 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 (!_texture.isEmpty()) { + glGenTextures(1, &_textureTextureID); + glBindTexture(GL_TEXTURE_2D, _textureTextureID); + 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); + int textureSize = glm::sqrt(_texture.size()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, textureSize, textureSize, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, _texture.constData()); + } } // create the buffer objects lazily int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER; @@ -998,7 +1019,8 @@ private: HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector(), lod), + AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), QVector(), lod), _intersections(intersections) { } @@ -1065,11 +1087,11 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { int colorSize = _buffer->getColorSize(); if (colorSize == 0) { - return STOP_RECURSION; + continue; } HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); if (!color) { - return STOP_RECURSION; + continue; } const Box& colorBounds = _buffer->getColorBounds(); overlap = colorBounds.getIntersection(overlap); @@ -1113,6 +1135,50 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { } } } + + int textureSize = _buffer->getTextureSize(); + if (textureSize == 0) { + continue; + } + HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue(); + if (!texture) { + continue; + } + const Box& textureBounds = _buffer->getTextureBounds(); + overlap = textureBounds.getIntersection(overlap); + float textureIncrement = _buffer->getTextureIncrement(); + destX = (overlap.minimum.x - textureBounds.minimum.x) / textureIncrement; + destY = (overlap.minimum.z - textureBounds.minimum.z) / textureIncrement; + destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / textureIncrement); + destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / textureIncrement); + dest = _buffer->getTexture().data() + destY * textureSize + destX; + + const QByteArray& srcTexture = texture->getContents(); + srcSize = glm::sqrt(srcTexture.size()); + srcIncrement = info.size / srcSize; + + if (srcIncrement == textureIncrement) { + // 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 = srcTexture.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += textureSize) { + 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 = textureIncrement / srcIncrement; + for (int y = 0; y < destHeight; y++, dest += textureSize, srcY += srcAdvance) { + const char* src = srcTexture.constData() + (int)srcY * srcSize; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { + *lineDest = src[(int)lineSrcX]; + } + } + } } return STOP_RECURSION; } @@ -1138,6 +1204,7 @@ private: HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() << 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)), @@ -1165,18 +1232,28 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { colorContentsSize = extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES; } + HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue(); + int textureContentsSize = 0; + if (texture) { + const QByteArray& textureContents = texture->getContents(); + int textureSize = glm::sqrt(textureContents.size()); + int extendedTextureSize = textureSize + HeightfieldBuffer::SHARED_EDGE; + textureContentsSize = extendedTextureSize * extendedTextureSize; + } + const HeightfieldBuffer* existingBuffer = static_cast( - info.inputValues.at(2).getInlineValue().data()); + info.inputValues.at(3).getInlineValue().data()); Box bounds = info.getBounds(); if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize) { + existingBuffer->getColor().size() == colorContentsSize && + existingBuffer->getTexture().size() == textureContentsSize) { // 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)); + QByteArray(colorContentsSize, 0), QByteArray(textureContentsSize, 0)); const Box& heightBounds = buffer->getHeightBounds(); addRegion(bounds, heightBounds); @@ -1249,7 +1326,7 @@ int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, - buffer->getHeight(), buffer->getColor()); + buffer->getHeight(), buffer->getColor(), buffer->getTexture()); _fetchVisitor.init(newBuffer); _data->guide(_fetchVisitor); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer))); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 38d67bcaed..206c378fd1 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -49,7 +49,7 @@ public: Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); - Q_INVOKABLE void deleteTextures(int heightID, int colorID); + Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID); protected: @@ -139,7 +139,8 @@ public: static const int SHARED_EDGE; static const int HEIGHT_EXTENSION; - HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color); + HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, + const QByteArray& color, const QByteArray& texture); ~HeightfieldBuffer(); const glm::vec3& getTranslation() const { return _translation; } @@ -147,6 +148,7 @@ public: const Box& getHeightBounds() const { return _heightBounds; } const Box& getColorBounds() const { return _colorBounds; } + const Box& getTextureBounds() const { return _textureBounds; } QByteArray& getHeight() { return _height; } const QByteArray& getHeight() const { return _height; } @@ -154,6 +156,9 @@ public: QByteArray& getColor() { return _color; } const QByteArray& getColor() const { return _color; } + QByteArray& getTexture() { return _texture; } + const QByteArray& getTexture() const { return _texture; } + QByteArray getUnextendedHeight() const; QByteArray getUnextendedColor() const; @@ -163,6 +168,9 @@ public: int getColorSize() const { return _colorSize; } float getColorIncrement() const { return _colorIncrement; } + int getTextureSize() const { return _textureSize; } + float getTextureIncrement() const { return _textureIncrement; } + virtual void render(bool cursor = false); private: @@ -171,15 +179,20 @@ private: float _scale; Box _heightBounds; Box _colorBounds; + Box _textureBounds; QByteArray _height; QByteArray _color; + QByteArray _texture; GLuint _heightTextureID; GLuint _colorTextureID; + GLuint _textureTextureID; int _heightSize; float _heightIncrement; int _colorSize; float _colorIncrement; - + int _textureSize; + float _textureIncrement; + typedef QPair BufferPair; static QHash _bufferPairs; }; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 6251448dce..2c99caf7d2 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1049,7 +1049,8 @@ void ImportHeightfieldTool::updatePreview() { columns * HeightfieldData::COLOR_BYTES); } } - buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); + buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, + height, color, QByteArray()))); } } } From 416d9bac2ef25313f6ccd78b794b8cbfdbc46a91 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 20 Aug 2014 14:48:02 -0700 Subject: [PATCH 09/15] Working on texture rendering. --- .../shaders/metavoxel_heightfield_base.frag | 20 ++ .../shaders/metavoxel_heightfield_base.vert | 33 +++ .../shaders/metavoxel_heightfield_light.frag | 21 ++ .../shaders/metavoxel_heightfield_light.vert | 45 +++ ...heightfield_light_cascaded_shadow_map.frag | 44 +++ ...etavoxel_heightfield_light_shadow_map.frag | 33 +++ .../shaders/metavoxel_heightfield_splat.frag | 20 ++ .../shaders/metavoxel_heightfield_splat.vert | 33 +++ interface/src/MetavoxelSystem.cpp | 260 ++++++++++++------ interface/src/MetavoxelSystem.h | 49 +++- interface/src/renderer/TextureCache.cpp | 7 +- interface/src/renderer/TextureCache.h | 4 +- interface/src/ui/MetavoxelEditor.cpp | 6 +- .../metavoxels/src/MetavoxelMessages.cpp | 2 +- 14 files changed, 473 insertions(+), 104 deletions(-) create mode 100644 interface/resources/shaders/metavoxel_heightfield_base.frag create mode 100644 interface/resources/shaders/metavoxel_heightfield_base.vert create mode 100644 interface/resources/shaders/metavoxel_heightfield_light.frag create mode 100644 interface/resources/shaders/metavoxel_heightfield_light.vert create mode 100644 interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag create mode 100644 interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag create mode 100644 interface/resources/shaders/metavoxel_heightfield_splat.frag create mode 100644 interface/resources/shaders/metavoxel_heightfield_splat.vert diff --git a/interface/resources/shaders/metavoxel_heightfield_base.frag b/interface/resources/shaders/metavoxel_heightfield_base.frag new file mode 100644 index 0000000000..9b64a59e6f --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_base.frag @@ -0,0 +1,20 @@ +#version 120 + +// +// metavoxel_heightfield_base.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +void main(void) { + // compute the base color based on OpenGL lighting model + gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_base.vert b/interface/resources/shaders/metavoxel_heightfield_base.vert new file mode 100644 index 0000000000..3e4b081d6f --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_base.vert @@ -0,0 +1,33 @@ +#version 120 + +// +// metavoxel_heightfield_base.vert +// vertex shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the height texture +uniform sampler2D heightMap; + +// the distance between height points in texture space +uniform float heightScale; + +// the scale between height and color textures +uniform float colorScale; + +void main(void) { + // add the height to the position + float height = texture2D(heightMap, gl_MultiTexCoord0.st).r; + gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); + + // the zero height should be invisible + gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); + + // pass along the scaled/offset texture coordinates + gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale; +} diff --git a/interface/resources/shaders/metavoxel_heightfield_light.frag b/interface/resources/shaders/metavoxel_heightfield_light.frag new file mode 100644 index 0000000000..ce3f23e142 --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_light.frag @@ -0,0 +1,21 @@ +#version 120 + +// +// metavoxel_heightfield_light.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // compute the base color based on OpenGL lighting model + gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position))); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_light.vert b/interface/resources/shaders/metavoxel_heightfield_light.vert new file mode 100644 index 0000000000..228d575b81 --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_light.vert @@ -0,0 +1,45 @@ +#version 120 + +// +// metavoxel_heighfield_light.vert +// vertex shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the height texture +uniform sampler2D heightMap; + +// the distance between height points in texture space +uniform float heightScale; + +// the interpolated position +varying vec4 position; + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // transform and store the normal for interpolation + vec2 heightCoord = gl_MultiTexCoord0.st; + float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r - + texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r; + float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r - + texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r; + normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0)); + + // add the height to the position + float height = texture2D(heightMap, heightCoord).r; + position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); + gl_Position = gl_ProjectionMatrix * position; + + // the zero height should be invisible + gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); + + // and the shadow texture coordinates + gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag b/interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag new file mode 100644 index 0000000000..73382eb83c --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag @@ -0,0 +1,44 @@ +#version 120 + +// +// metavoxel_heightfield_light_cascaded_shadow_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the shadow texture +uniform sampler2DShadow shadowMap; + +// the distances to the cascade sections +uniform vec3 shadowDistances; + +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; + +// the interpolated position +varying vec4 position; + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // compute the index of the cascade to use and the corresponding texture coordinates + int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0))); + vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), + dot(gl_EyePlaneR[shadowIndex], position)); + + // compute the base color based on OpenGL lighting model + float diffuse = dot(normalize(normal), gl_LightSource[0].position); + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); + gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag b/interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag new file mode 100644 index 0000000000..4f2df8958b --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag @@ -0,0 +1,33 @@ +#version 120 + +// +// metavoxel_heightfield_light_shadow_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the shadow texture +uniform sampler2DShadow shadowMap; + +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // compute the base color based on OpenGL lighting model + float diffuse = dot(normalize(normal), gl_LightSource[0].position); + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); + gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag new file mode 100644 index 0000000000..7b73cf331b --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_splat.frag @@ -0,0 +1,20 @@ +#version 120 + +// +// metavoxel_heightfield_splat.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +void main(void) { + // compute the base color based on OpenGL lighting model + gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.vert b/interface/resources/shaders/metavoxel_heightfield_splat.vert new file mode 100644 index 0000000000..0f36605454 --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield_splat.vert @@ -0,0 +1,33 @@ +#version 120 + +// +// metavoxel_heighfield_splat.vert +// vertex shader +// +// Created by Andrzej Kapolka on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the height texture +uniform sampler2D heightMap; + +// the distance between height points in texture space +uniform float heightScale; + +// the scale between height and texture textures +uniform float textureScale; + +void main(void) { + // add the height to the position + float height = texture2D(heightMap, gl_MultiTexCoord0.st).r; + gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); + + // the zero height should be invisible + gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); + + // pass along the scaled/offset texture coordinates + gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * textureScale; +} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4cdd2498e6..41ae254c71 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -602,24 +602,23 @@ 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, const QByteArray& texture) : + const QByteArray& height, const QByteArray& color, const QByteArray& texture, + const QVector& textures) : _translation(translation), _scale(scale), _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), _colorBounds(_heightBounds), - _textureBounds(_heightBounds), _height(height), _color(color), _texture(texture), + _textures(textures), _heightTextureID(0), _colorTextureID(0), _textureTextureID(0), _heightSize(glm::sqrt(height.size())), _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), _colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)), - _colorIncrement(scale / (_colorSize - SHARED_EDGE)), - _textureSize(glm::sqrt(texture.size())), - _textureIncrement(scale / (_textureSize - SHARED_EDGE)) { + _colorIncrement(scale / (_colorSize - SHARED_EDGE)) { _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; @@ -628,9 +627,6 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; - - _textureBounds.maximum.x += _textureIncrement * SHARED_EDGE; - _textureBounds.maximum.z += _textureIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { @@ -712,6 +708,15 @@ void HeightfieldBuffer::render(bool cursor) { int textureSize = glm::sqrt(_texture.size()); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, textureSize, textureSize, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, _texture.constData()); + + _networkTextures.resize(_textures.size()); + for (int i = 0; i < _textures.size(); i++) { + const SharedObjectPointer texture = _textures.at(i); + if (texture) { + _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( + static_cast(texture.data())->getURL()); + } + } } } // create the buffer objects lazily @@ -780,7 +785,56 @@ void HeightfieldBuffer::render(bool cursor) { glBindTexture(GL_TEXTURE_2D, _heightTextureID); - if (!cursor) { + if (cursor) { + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + } else if (!_textures.isEmpty()) { + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + glDepthFunc(GL_LEQUAL); + glEnable(GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + glBindTexture(GL_TEXTURE_2D, 0); + + if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) { + DefaultMetavoxelRendererImplementation::getShadowLightHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getShadowLightHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getShadowLightHeightScaleLocation(), 1.0f / _heightSize); + + } else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightScaleLocation(), 1.0f / _heightSize); + + } else { + DefaultMetavoxelRendererImplementation::getLightHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getLightHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); + } + + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind(); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glDepthFunc(GL_LESS); + + glActiveTexture(GL_TEXTURE0); + + } else { int heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation(); int colorScaleLocation = DefaultMetavoxelRendererImplementation::getColorScaleLocation(); ProgramObject* program = &DefaultMetavoxelRendererImplementation::getHeightfieldProgram(); @@ -798,11 +852,9 @@ void HeightfieldBuffer::render(bool cursor) { program->setUniformValue(colorScaleLocation, (float)_heightSize / innerSize); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _colorTextureID); - } - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - if (!cursor) { + + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); } @@ -919,6 +971,68 @@ void DefaultMetavoxelRendererImplementation::init() { _shadowDistancesLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("shadowDistances"); _cascadedShadowMapHeightfieldProgram.release(); + _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_base.vert"); + _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_base.frag"); + _baseHeightfieldProgram.link(); + + _baseHeightfieldProgram.bind(); + _baseHeightfieldProgram.setUniformValue("heightMap", 0); + _baseHeightfieldProgram.setUniformValue("diffuseMap", 1); + _baseHeightScaleLocation = _heightfieldProgram.uniformLocation("heightScale"); + _baseColorScaleLocation = _heightfieldProgram.uniformLocation("colorScale"); + _baseHeightfieldProgram.release(); + + _splatHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_splat.vert"); + _splatHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_splat.frag"); + _splatHeightfieldProgram.link(); + + _splatHeightfieldProgram.bind(); + _splatHeightfieldProgram.setUniformValue("heightMap", 0); + _splatHeightfieldProgram.setUniformValue("diffuseMap", 1); + _splatHeightScaleLocation = _splatHeightfieldProgram.uniformLocation("heightScale"); + _splatTextureScaleLocation = _splatHeightfieldProgram.uniformLocation("textureScale"); + _splatHeightfieldProgram.release(); + + _lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_light.vert"); + _lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_light.frag"); + _lightHeightfieldProgram.link(); + + _lightHeightfieldProgram.bind(); + _lightHeightfieldProgram.setUniformValue("heightMap", 0); + _lightHeightScaleLocation = _lightHeightfieldProgram.uniformLocation("heightScale"); + _lightHeightfieldProgram.release(); + + _shadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_light.vert"); + _shadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_light_shadow_map.frag"); + _shadowLightHeightfieldProgram.link(); + + _shadowLightHeightfieldProgram.bind(); + _shadowLightHeightfieldProgram.setUniformValue("heightMap", 0); + _shadowLightHeightfieldProgram.setUniformValue("shadowMap", 2); + _shadowLightHeightScaleLocation = _shadowLightHeightfieldProgram.uniformLocation("heightScale"); + _shadowLightHeightfieldProgram.release(); + + _cascadedShadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_light.vert"); + _cascadedShadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag"); + _cascadedShadowLightHeightfieldProgram.link(); + + _cascadedShadowLightHeightfieldProgram.bind(); + _cascadedShadowLightHeightfieldProgram.setUniformValue("heightMap", 0); + _cascadedShadowLightHeightfieldProgram.setUniformValue("shadowMap", 2); + _cascadedShadowLightHeightScaleLocation = _cascadedShadowLightHeightfieldProgram.uniformLocation("heightScale"); + _shadowLightDistancesLocation = _cascadedShadowLightHeightfieldProgram.uniformLocation("shadowDistances"); + _cascadedShadowLightHeightfieldProgram.release(); + _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_heightfield_cursor.vert"); _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + @@ -1019,8 +1133,7 @@ private: HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), QVector(), lod), + AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector(), lod), _intersections(intersections) { } @@ -1135,50 +1248,6 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { } } } - - int textureSize = _buffer->getTextureSize(); - if (textureSize == 0) { - continue; - } - HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue(); - if (!texture) { - continue; - } - const Box& textureBounds = _buffer->getTextureBounds(); - overlap = textureBounds.getIntersection(overlap); - float textureIncrement = _buffer->getTextureIncrement(); - destX = (overlap.minimum.x - textureBounds.minimum.x) / textureIncrement; - destY = (overlap.minimum.z - textureBounds.minimum.z) / textureIncrement; - destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / textureIncrement); - destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / textureIncrement); - dest = _buffer->getTexture().data() + destY * textureSize + destX; - - const QByteArray& srcTexture = texture->getContents(); - srcSize = glm::sqrt(srcTexture.size()); - srcIncrement = info.size / srcSize; - - if (srcIncrement == textureIncrement) { - // 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 = srcTexture.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += textureSize) { - 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 = textureIncrement / srcIncrement; - for (int y = 0; y < destHeight; y++, dest += textureSize, srcY += srcAdvance) { - const char* src = srcTexture.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - *lineDest = src[(int)lineSrcX]; - } - } - } } return STOP_RECURSION; } @@ -1233,42 +1302,43 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { } HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue(); - int textureContentsSize = 0; + QByteArray textureContents; + QVector textures; if (texture) { - const QByteArray& textureContents = texture->getContents(); - int textureSize = glm::sqrt(textureContents.size()); - int extendedTextureSize = textureSize + HeightfieldBuffer::SHARED_EDGE; - textureContentsSize = extendedTextureSize * extendedTextureSize; + textureContents = texture->getContents(); + textures = texture->getTextures(); } const HeightfieldBuffer* existingBuffer = static_cast( info.inputValues.at(3).getInlineValue().data()); Box bounds = info.getBounds(); if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize && - existingBuffer->getTexture().size() == textureContentsSize) { + existingBuffer->getColor().size() == colorContentsSize) { // we already have a buffer of the correct resolution addRegion(bounds, existingBuffer->getHeightBounds()); - return STOP_RECURSION; + buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), + existingBuffer->getColor(), textureContents, textures); + + } else { + // we must create a new buffer and update its borders + buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), + QByteArray(colorContentsSize, 0), textureContents, textures); + 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); } - // we must create a new buffer and update its borders - buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), QByteArray(textureContentsSize, 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; @@ -1326,7 +1396,7 @@ int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, - buffer->getHeight(), buffer->getColor(), buffer->getTexture()); + buffer->getHeight(), buffer->getColor(), buffer->getTexture(), buffer->getTextures()); _fetchVisitor.init(newBuffer); _data->guide(_fetchVisitor); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer))); @@ -1514,6 +1584,9 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox ProgramObject* program = &_heightfieldProgram; if (Menu::getInstance()->getShadowsEnabled()) { if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + _cascadedShadowLightHeightfieldProgram.bind(); + _cascadedShadowLightHeightfieldProgram.setUniform(_shadowLightDistancesLocation, + Application::getInstance()->getShadowDistances()); program = &_cascadedShadowMapHeightfieldProgram; program->bind(); program->setUniform(_shadowDistancesLocation, Application::getInstance()->getShadowDistances()); @@ -1559,6 +1632,19 @@ ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightfi int DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightScaleLocation; int DefaultMetavoxelRendererImplementation::_cascadedShadowMapColorScaleLocation; int DefaultMetavoxelRendererImplementation::_shadowDistancesLocation; +ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram; +int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation; +int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation; +ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram; +int DefaultMetavoxelRendererImplementation::_splatHeightScaleLocation; +int DefaultMetavoxelRendererImplementation::_splatTextureScaleLocation; +ProgramObject DefaultMetavoxelRendererImplementation::_lightHeightfieldProgram; +int DefaultMetavoxelRendererImplementation::_lightHeightScaleLocation; +ProgramObject DefaultMetavoxelRendererImplementation::_shadowLightHeightfieldProgram; +int DefaultMetavoxelRendererImplementation::_shadowLightHeightScaleLocation; +ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowLightHeightfieldProgram; +int DefaultMetavoxelRendererImplementation::_cascadedShadowLightHeightScaleLocation; +int DefaultMetavoxelRendererImplementation::_shadowLightDistancesLocation; ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 206c378fd1..dc2ff8633a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -140,7 +140,8 @@ public: static const int HEIGHT_EXTENSION; HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, - const QByteArray& color, const QByteArray& texture); + const QByteArray& color, const QByteArray& texture = QByteArray(), + const QVector& textures = QVector()); ~HeightfieldBuffer(); const glm::vec3& getTranslation() const { return _translation; } @@ -148,7 +149,6 @@ public: const Box& getHeightBounds() const { return _heightBounds; } const Box& getColorBounds() const { return _colorBounds; } - const Box& getTextureBounds() const { return _textureBounds; } QByteArray& getHeight() { return _height; } const QByteArray& getHeight() const { return _height; } @@ -159,6 +159,8 @@ public: QByteArray& getTexture() { return _texture; } const QByteArray& getTexture() const { return _texture; } + const QVector& getTextures() const { return _textures; } + QByteArray getUnextendedHeight() const; QByteArray getUnextendedColor() const; @@ -168,9 +170,6 @@ public: int getColorSize() const { return _colorSize; } float getColorIncrement() const { return _colorIncrement; } - int getTextureSize() const { return _textureSize; } - float getTextureIncrement() const { return _textureIncrement; } - virtual void render(bool cursor = false); private: @@ -179,19 +178,18 @@ private: float _scale; Box _heightBounds; Box _colorBounds; - Box _textureBounds; QByteArray _height; QByteArray _color; QByteArray _texture; + QVector _textures; GLuint _heightTextureID; GLuint _colorTextureID; GLuint _textureTextureID; + QVector _networkTextures; int _heightSize; float _heightIncrement; int _colorSize; float _colorIncrement; - int _textureSize; - float _textureIncrement; typedef QPair BufferPair; static QHash _bufferPairs; @@ -244,6 +242,23 @@ public: static int getCascadedShadowMapHeightScaleLocation() { return _cascadedShadowMapHeightScaleLocation; } static int getCascadedShadowMapColorScaleLocation() { return _cascadedShadowMapColorScaleLocation; } + static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; } + static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; } + static int getBaseColorScaleLocation() { return _baseColorScaleLocation; } + + static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; } + static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; } + static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; } + + static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; } + static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; } + + static ProgramObject& getShadowLightHeightfieldProgram() { return _shadowLightHeightfieldProgram; } + static int getShadowLightHeightScaleLocation() { return _shadowLightHeightScaleLocation; } + + static ProgramObject& getCascadedShadowLightHeightfieldProgram() { return _cascadedShadowLightHeightfieldProgram; } + static int getCascadedShadowLightHeightScaleLocation() { return _cascadedShadowLightHeightScaleLocation; } + static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; } Q_INVOKABLE DefaultMetavoxelRendererImplementation(); @@ -270,6 +285,24 @@ private: static int _cascadedShadowMapColorScaleLocation; static int _shadowDistancesLocation; + static ProgramObject _baseHeightfieldProgram; + static int _baseHeightScaleLocation; + static int _baseColorScaleLocation; + + static ProgramObject _splatHeightfieldProgram; + static int _splatHeightScaleLocation; + static int _splatTextureScaleLocation; + + static ProgramObject _lightHeightfieldProgram; + static int _lightHeightScaleLocation; + + static ProgramObject _shadowLightHeightfieldProgram; + static int _shadowLightHeightScaleLocation; + + static ProgramObject _cascadedShadowLightHeightfieldProgram; + static int _cascadedShadowLightHeightScaleLocation; + static int _shadowLightDistancesLocation; + static ProgramObject _heightfieldCursorProgram; }; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 11c4778c19..ee10d67ee9 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -179,15 +179,14 @@ public: const QByteArray& content; }; -QSharedPointer TextureCache::getTexture(const QUrl& url, bool normalMap, - bool dilatable, const QByteArray& content) { +NetworkTexturePointer TextureCache::getTexture(const QUrl& url, bool normalMap, bool dilatable, const QByteArray& content) { if (!dilatable) { TextureExtra extra = { normalMap, content }; return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast(); } - QSharedPointer texture = _dilatableNetworkTextures.value(url); + NetworkTexturePointer texture = _dilatableNetworkTextures.value(url); if (texture.isNull()) { - texture = QSharedPointer(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared); + texture = NetworkTexturePointer(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared); texture->setSelf(texture); texture->setCache(this); _dilatableNetworkTextures.insert(url, texture); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 58f9345c4a..06f724d70d 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -23,6 +23,8 @@ class QOpenGLFramebufferObject; class NetworkTexture; +typedef QSharedPointer NetworkTexturePointer; + /// Stores cached textures, including render-to-texture targets. class TextureCache : public ResourceCache { Q_OBJECT @@ -47,7 +49,7 @@ public: GLuint getBlueTextureID(); /// Loads a texture from the specified URL. - QSharedPointer getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false, + NetworkTexturePointer getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false, const QByteArray& content = QByteArray()); /// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 2c99caf7d2..3ca509cd95 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -974,7 +974,8 @@ void ImportHeightfieldTool::apply() { data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); - QByteArray texture(height.size(), 0); + int size = glm::sqrt(height.size()) + HeightfieldBuffer::SHARED_EDGE; + QByteArray texture(size * size, 0); HeightfieldTextureDataPointer texturePointer(new HeightfieldTextureData(texture)); data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer)))); @@ -1049,8 +1050,7 @@ void ImportHeightfieldTool::updatePreview() { columns * HeightfieldData::COLOR_BYTES); } } - buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, - height, color, QByteArray()))); + buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); } } } diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 33aea9fcb1..1a8f64d935 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -593,7 +593,7 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { } int size = glm::sqrt((float)contents.size()); int highest = size - 1; - float heightScale = size / info.size; + float heightScale = highest / info.size; glm::vec3 center = (_edit.position - info.minimum) * heightScale; float scaledRadius = _edit.radius * heightScale; From 72500630b014f5f45131253d67d3b24ea002fd49 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 20 Aug 2014 19:26:06 -0700 Subject: [PATCH 10/15] Basic splatting. --- .../shaders/metavoxel_heightfield_splat.frag | 16 +++- .../shaders/metavoxel_heightfield_splat.vert | 19 ++++- interface/src/MetavoxelSystem.cpp | 63 ++++++++++++++-- interface/src/MetavoxelSystem.h | 4 + .../metavoxels/src/AttributeRegistry.cpp | 74 ++++++++++++------- libraries/metavoxels/src/AttributeRegistry.h | 1 - 6 files changed, 138 insertions(+), 39 deletions(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag index 7b73cf331b..6a3c058b80 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.frag +++ b/interface/resources/shaders/metavoxel_heightfield_splat.frag @@ -11,10 +11,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// the diffuse texture -uniform sampler2D diffuseMap; +const int SPLAT_COUNT = 4; + +// the splat textures +uniform sampler2D diffuseMaps[SPLAT_COUNT]; + +// alpha values for the four splat textures +varying vec4 alphaValues; void main(void) { - // compute the base color based on OpenGL lighting model - gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st); + // blend the splat textures + gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + + texture2D(diffuseMaps[1], gl_TexCoord[0].st) * alphaValues.y + + texture2D(diffuseMaps[2], gl_TexCoord[0].st) * alphaValues.z + + texture2D(diffuseMaps[3], gl_TexCoord[0].st) * alphaValues.w); } diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.vert b/interface/resources/shaders/metavoxel_heightfield_splat.vert index 0f36605454..950162073e 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.vert +++ b/interface/resources/shaders/metavoxel_heightfield_splat.vert @@ -14,20 +14,37 @@ // the height texture uniform sampler2D heightMap; +// the texture that contains the texture indices +uniform sampler2D textureMap; + // the distance between height points in texture space uniform float heightScale; // the scale between height and texture textures uniform float textureScale; +// the lower bounds of the values corresponding to the splat textures +uniform vec4 textureValueMinima; + +// the upper bounds of the values corresponding to the splat textures +uniform vec4 textureValueMaxima; + +// alpha values for the four splat textures +varying vec4 alphaValues; + void main(void) { // add the height to the position float height = texture2D(heightMap, gl_MultiTexCoord0.st).r; gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); // the zero height should be invisible - gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); + gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0 - step(height, 0.0)); // pass along the scaled/offset texture coordinates gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * textureScale; + + // compute the alpha values for each texture + float value = texture2D(textureMap, gl_TexCoord[0].st).r; + vec4 valueVector = vec4(value, value, value, value); + alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima); } diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 41ae254c71..18d76b1543 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -673,13 +673,17 @@ public: glm::vec3 vertex; }; +const int SPLAT_COUNT = 4; +const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; + void HeightfieldBuffer::render(bool cursor) { // initialize textures, etc. on first render 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_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0, @@ -702,7 +706,8 @@ void HeightfieldBuffer::render(bool cursor) { if (!_texture.isEmpty()) { glGenTextures(1, &_textureTextureID); glBindTexture(GL_TEXTURE_2D, _textureTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); int textureSize = glm::sqrt(_texture.size()); @@ -800,11 +805,53 @@ void HeightfieldBuffer::render(bool cursor) { glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); glDepthFunc(GL_LEQUAL); + glDepthMask(false); glEnable(GL_BLEND); - glBlendFunc(GL_DST_COLOR, GL_ZERO); + glDisable(GL_ALPHA_TEST); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -1.0f); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getSplatHeightScaleLocation(), 1.0f / _heightSize); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getSplatTextureScaleLocation(), (float)_heightSize / innerSize); + + glBindTexture(GL_TEXTURE_2D, _textureTextureID); + const int TEXTURES_PER_SPLAT = 4; + for (int i = 0; i < _textures.size(); i += TEXTURES_PER_SPLAT) { + for (int j = 0; j < SPLAT_COUNT; j++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); + int index = i + j; + if (index < _networkTextures.size()) { + const NetworkTexturePointer& texture = _networkTextures.at(index); + glBindTexture(GL_TEXTURE_2D, texture ? texture->getID() : 0); + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } + } + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getSplatTextureValueMinimaLocation(), + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getSplatTextureValueMaximaLocation(), + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + } + + glEnable(GL_ALPHA_TEST); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) { @@ -831,7 +878,8 @@ void HeightfieldBuffer::render(bool cursor) { glDisable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glDepthFunc(GL_LESS); - + glDepthMask(true); + glActiveTexture(GL_TEXTURE0); } else { @@ -992,9 +1040,12 @@ void DefaultMetavoxelRendererImplementation::init() { _splatHeightfieldProgram.bind(); _splatHeightfieldProgram.setUniformValue("heightMap", 0); - _splatHeightfieldProgram.setUniformValue("diffuseMap", 1); + _splatHeightfieldProgram.setUniformValue("textureMap", 1); + _splatHeightfieldProgram.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); _splatHeightScaleLocation = _splatHeightfieldProgram.uniformLocation("heightScale"); _splatTextureScaleLocation = _splatHeightfieldProgram.uniformLocation("textureScale"); + _splatTextureValueMinimaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMinima"); + _splatTextureValueMaximaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMaxima"); _splatHeightfieldProgram.release(); _lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + @@ -1638,6 +1689,8 @@ int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram; int DefaultMetavoxelRendererImplementation::_splatHeightScaleLocation; int DefaultMetavoxelRendererImplementation::_splatTextureScaleLocation; +int DefaultMetavoxelRendererImplementation::_splatTextureValueMinimaLocation; +int DefaultMetavoxelRendererImplementation::_splatTextureValueMaximaLocation; ProgramObject DefaultMetavoxelRendererImplementation::_lightHeightfieldProgram; int DefaultMetavoxelRendererImplementation::_lightHeightScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_shadowLightHeightfieldProgram; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index dc2ff8633a..752a060b86 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -249,6 +249,8 @@ public: static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; } static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; } static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; } + static int getSplatTextureValueMinimaLocation() { return _splatTextureValueMinimaLocation; } + static int getSplatTextureValueMaximaLocation() { return _splatTextureValueMaximaLocation; } static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; } static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; } @@ -292,6 +294,8 @@ private: static ProgramObject _splatHeightfieldProgram; static int _splatHeightScaleLocation; static int _splatTextureScaleLocation; + static int _splatTextureValueMinimaLocation; + static int _splatTextureValueMaximaLocation; static ProgramObject _lightHeightfieldProgram; static int _lightHeightScaleLocation; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c09848c201..94f0abaee4 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -932,6 +932,29 @@ void HeightfieldColorData::set(const QImage& image) { memcpy(_contents.data(), image.constBits(), _contents.size()); } +const int TEXTURE_HEADER_SIZE = sizeof(qint32) * 4; + +static QByteArray encodeTexture(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(TEXTURE_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + inflated.append(contents); + return qCompress(inflated); +} + +static QByteArray decodeTexture(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + return inflated.mid(TEXTURE_HEADER_SIZE); +} + HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector& textures) : HeightfieldData(contents), _textures(textures) { @@ -951,32 +974,31 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H in.readDelta(_textures, reference->getTextures()); reference->setDeltaData(HeightfieldDataPointer(this)); _contents = reference->getContents(); - QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); - if (image.isNull()) { + + int offsetX, offsetY, width, height; + QByteArray delta = decodeTexture(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { return; } - QPoint offset = image.offset(); - if (offset.x() == 0) { - set(image); + if (offsetX == 0) { + _contents = delta; return; } - int minX = offset.x() - 1; - int minY = offset.y() - 1; + int minX = offsetX - 1; + int minY = offsetY - 1; int size = glm::sqrt((float)_contents.size()); + const char* src = delta.constData(); char* dest = _contents.data() + minY * size + minX; - for (int y = 0; y < image.height(); y++) { - memcpy(dest, image.constScanLine(y), image.width()); - dest += size; + for (int y = 0; y < height; y++, src += width, dest += size) { + memcpy(dest, src, width); } } void HeightfieldTextureData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - QImage image; int size = glm::sqrt((float)_contents.size()); - image = QImage((uchar*)_contents.data(), size, size, QImage::Format_Indexed8); - _encoded = encodeHeightfieldImage(image, true); + _encoded = encodeTexture(0, 0, size, size, _contents); } out << _encoded.size(); out.writeAligned(_encoded); @@ -990,7 +1012,6 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture } QMutexLocker locker(&reference->getEncodedDeltaMutex()); if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - QImage image; int size = glm::sqrt((float)_contents.size()); int minX = size, minY = size; int maxX = -1, maxY = -1; @@ -1010,18 +1031,19 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture maxY = qMax(maxY, y); } } + QByteArray delta; + int width = 0, height = 0; if (maxX >= minX) { - int width = maxX - minX + 1; - int height = maxY - minY + 1; - image = QImage(width, height, QImage::Format_Indexed8); + width = maxX - minX + 1; + height = maxY - minY + 1; + delta = QByteArray(width * height, 0); + char* dest = delta.data(); src = _contents.constData() + minY * size + minX; - for (int y = 0; y < height; y++) { - memcpy(image.scanLine(y), src, width); - src += size; + for (int y = 0; y < height; y++, src += size, dest += width) { + memcpy(dest, src, width); } } - image.setOffset(QPoint(minX + 1, minY + 1)); - reference->setEncodedDelta(encodeHeightfieldImage(image, true)); + reference->setEncodedDelta(encodeTexture(minX + 1, minY + 1, width, height, delta)); reference->setDeltaData(HeightfieldDataPointer(this)); } out << reference->getEncodedDelta().size(); @@ -1030,15 +1052,11 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture } void HeightfieldTextureData::read(Bitstream& in, int bytes) { - set(decodeHeightfieldImage(_encoded = in.readAligned(bytes))); + int offsetX, offsetY, width, height; + _contents = decodeTexture(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); in >> _textures; } -void HeightfieldTextureData::set(const QImage& image) { - _contents.resize(image.width() * image.height()); - memcpy(_contents.data(), image.constBits(), _contents.size()); -} - HeightfieldTexture::HeightfieldTexture() { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index f3e6dc0b97..fb6ff5097d 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -540,7 +540,6 @@ public: private: void read(Bitstream& in, int bytes); - void set(const QImage& image); QVector _textures; }; From 74b39773d601df830dc44959240377d471a35a01 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 21 Aug 2014 14:50:58 -0700 Subject: [PATCH 11/15] Don't merge after expansion; it breaks stuff. --- libraries/metavoxels/src/AttributeRegistry.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 94f0abaee4..e823e081cc 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -274,9 +274,7 @@ MetavoxelNode* Attribute::expandMetavoxelRoot(const MetavoxelNode& root) { MetavoxelNode* newGrandchild = new MetavoxelNode(attribute); newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild); } - newChild->mergeChildren(attribute); } - newParent->mergeChildren(attribute); return newParent; } @@ -1501,9 +1499,7 @@ MetavoxelNode* SharedObjectSetAttribute::expandMetavoxelRoot(const MetavoxelNode MetavoxelNode* newGrandchild = new MetavoxelNode(attribute); newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild); } - newChild->mergeChildren(attribute); } - newParent->mergeChildren(attribute); return newParent; } From e152e0c2bea918a7e117bf844640bed26ee5c9df Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 21 Aug 2014 15:22:34 -0700 Subject: [PATCH 12/15] Detachment fix. --- interface/src/ui/MetavoxelEditor.cpp | 4 +++- libraries/metavoxels/src/SharedObject.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 3ca509cd95..ce09e3657d 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1188,7 +1188,9 @@ QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) { if (alternate) { return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); } else { - return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), _textureEditor->getObject(), + SharedObjectPointer texture = _textureEditor->getObject(); + _textureEditor->detachObject(); + return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), texture, _texture ? _texture->getAverageColor() : QColor())); } } diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index c125e1352b..bf9b123a36 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -193,7 +193,9 @@ void SharedObjectEditor::detachObject() { for (int i = 0; i < form->rowCount(); i++) { QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); QMetaProperty property = metaObject->property(widget->property("propertyIndex").toInt()); - connect(_object.data(), signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); + if (property.hasNotifySignal()) { + connect(_object.data(), signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); + } } } From 6e06b637096fb60c3632e7c68123568bb5ca2fd4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 21 Aug 2014 16:33:09 -0700 Subject: [PATCH 13/15] For splat textures, use transparent white as a default. --- interface/src/MetavoxelSystem.cpp | 2 +- interface/src/renderer/GeometryCache.cpp | 9 ++++--- interface/src/renderer/TextureCache.cpp | 34 ++++++++++++++++++------ interface/src/renderer/TextureCache.h | 6 +++-- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 18d76b1543..9a143706ed 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -719,7 +719,7 @@ void HeightfieldBuffer::render(bool cursor) { const SharedObjectPointer texture = _textures.at(i); if (texture) { _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( - static_cast(texture.data())->getURL()); + static_cast(texture.data())->getURL(), SPLAT_TEXTURE); } } } diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index b5bd63ab87..3cfc5efd5f 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -593,17 +593,20 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { NetworkMeshPart networkPart; if (!part.diffuseTexture.filename.isEmpty()) { networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture( - _textureBase.resolved(QUrl(part.diffuseTexture.filename)), false, mesh.isEye, part.diffuseTexture.content); + _textureBase.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, + mesh.isEye, part.diffuseTexture.content); networkPart.diffuseTexture->setLoadPriorities(_loadPriorities); } if (!part.normalTexture.filename.isEmpty()) { networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture( - _textureBase.resolved(QUrl(part.normalTexture.filename)), true, false, part.normalTexture.content); + _textureBase.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, + false, part.normalTexture.content); networkPart.normalTexture->setLoadPriorities(_loadPriorities); } if (!part.specularTexture.filename.isEmpty()) { networkPart.specularTexture = Application::getInstance()->getTextureCache()->getTexture( - _textureBase.resolved(QUrl(part.specularTexture.filename)), true, false, part.specularTexture.content); + _textureBase.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, + false, part.specularTexture.content); networkPart.specularTexture->setLoadPriorities(_loadPriorities); } networkMesh.parts.append(networkPart); diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index ee10d67ee9..c3e58d52bb 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -145,6 +145,8 @@ GLuint TextureCache::getPermutationNormalTextureID() { } const unsigned char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; +const unsigned char TRANSPARENT_WHITE[] = { 0xFF, 0xFF, 0xFF, 0x0 }; +const unsigned char OPAQUE_BLACK[] = { 0x0, 0x0, 0x0, 0xFF }; const unsigned char OPAQUE_BLUE[] = { 0x80, 0x80, 0xFF, 0xFF }; static void loadSingleColorTexture(const unsigned char* color) { @@ -175,13 +177,13 @@ GLuint TextureCache::getBlueTextureID() { /// Extra data for creating textures. class TextureExtra { public: - bool normalMap; + TextureType type; const QByteArray& content; }; -NetworkTexturePointer TextureCache::getTexture(const QUrl& url, bool normalMap, bool dilatable, const QByteArray& content) { +NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, bool dilatable, const QByteArray& content) { if (!dilatable) { - TextureExtra extra = { normalMap, content }; + TextureExtra extra = { type, content }; return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast(); } NetworkTexturePointer texture = _dilatableNetworkTextures.value(url); @@ -292,7 +294,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) { QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { const TextureExtra* textureExtra = static_cast(extra); - return QSharedPointer(new NetworkTexture(url, textureExtra->normalMap, textureExtra->content), + return QSharedPointer(new NetworkTexture(url, textureExtra->type, textureExtra->content), &Resource::allReferencesCleared); } @@ -316,7 +318,7 @@ Texture::~Texture() { glDeleteTextures(1, &_id); } -NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap, const QByteArray& content) : +NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : Resource(url, !content.isEmpty()), _translucent(false) { @@ -324,9 +326,25 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap, const QByteArray _loaded = true; } - // default to white/blue + // default to white/blue/black glBindTexture(GL_TEXTURE_2D, getID()); - loadSingleColorTexture(normalMap ? OPAQUE_BLUE : OPAQUE_WHITE); + switch (type) { + case NORMAL_TEXTURE: + loadSingleColorTexture(OPAQUE_BLUE); + break; + + case SPECULAR_TEXTURE: + loadSingleColorTexture(OPAQUE_BLACK); + break; + + case SPLAT_TEXTURE: + loadSingleColorTexture(TRANSPARENT_WHITE); + break; + + default: + loadSingleColorTexture(OPAQUE_WHITE); + break; + } glBindTexture(GL_TEXTURE_2D, 0); // if we have content, load it after we have our self pointer @@ -465,7 +483,7 @@ void NetworkTexture::imageLoaded(const QImage& image) { } DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url, const QByteArray& content) : - NetworkTexture(url, false, content), + NetworkTexture(url, DEFAULT_TEXTURE, content), _innerRadius(0), _outerRadius(0) { diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 06f724d70d..83d9b6cc74 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -25,6 +25,8 @@ class NetworkTexture; typedef QSharedPointer NetworkTexturePointer; +enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, SPECULAR_TEXTURE, SPLAT_TEXTURE }; + /// Stores cached textures, including render-to-texture targets. class TextureCache : public ResourceCache { Q_OBJECT @@ -49,7 +51,7 @@ public: GLuint getBlueTextureID(); /// Loads a texture from the specified URL. - NetworkTexturePointer getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false, + NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false, const QByteArray& content = QByteArray()); /// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is @@ -123,7 +125,7 @@ class NetworkTexture : public Resource, public Texture { public: - NetworkTexture(const QUrl& url, bool normalMap, const QByteArray& content); + NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); /// Checks whether it "looks like" this texture is translucent /// (majority of pixels neither fully opaque or fully transparent). From 5c77317cfd6e6ce4508c5d870959d955b3352dce Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 21 Aug 2014 18:07:30 -0700 Subject: [PATCH 14/15] Fix for flashing when painting textures. --- interface/src/MetavoxelSystem.cpp | 12 +++++++++--- interface/src/MetavoxelSystem.h | 5 ++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9a143706ed..3864b0537f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -118,7 +118,7 @@ void MetavoxelSystem::render() { viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); - guideToAugmented(renderVisitor); + guideToAugmented(renderVisitor, true); } class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { @@ -459,13 +459,19 @@ MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { return new MetavoxelSystemClient(node, _updater); } -void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor) { +void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelSystemClient* client = static_cast(node->getLinkedData()); if (client) { - client->getAugmentedData().guide(visitor); + MetavoxelData data = client->getAugmentedData(); + data.guide(visitor); + if (render) { + // save the rendered augmented data so that its cached texture references, etc., don't + // get collected when we replace it with more recent versions + client->setRenderedAugmentedData(data); + } } } } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 752a060b86..ac4dd430fb 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -57,7 +57,7 @@ protected: private: - void guideToAugmented(MetavoxelVisitor& visitor); + void guideToAugmented(MetavoxelVisitor& visitor, bool render = false); AttributePointer _pointBufferAttribute; AttributePointer _heightfieldBufferAttribute; @@ -92,6 +92,8 @@ public: /// Returns a copy of the augmented data. This function is thread-safe. MetavoxelData getAugmentedData(); + void setRenderedAugmentedData(const MetavoxelData& data) { _renderedAugmentedData = data; } + virtual int parseData(const QByteArray& packet); protected: @@ -102,6 +104,7 @@ protected: private: MetavoxelData _augmentedData; + MetavoxelData _renderedAugmentedData; QReadWriteLock _augmentedDataLock; }; From e9e367171056e5ca7b22d8445b580f4d109b5822 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 21 Aug 2014 18:14:10 -0700 Subject: [PATCH 15/15] Increment the version number. --- libraries/networking/src/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 1b48a2e333..619f652e1e 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 1; + return 2; default: return 0; }