Rather than using a boolean to indicate "colorness," use a different class.

More texture bits.
This commit is contained in:
Andrzej Kapolka 2014-08-19 14:20:47 -07:00
parent 23a4c21ce1
commit e854656a7f
9 changed files with 465 additions and 287 deletions

View file

@ -1011,7 +1011,7 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf && info.size > _buffer->getScale()) { if (!info.isLeaf && info.size > _buffer->getScale()) {
return DEFAULT_ORDER; return DEFAULT_ORDER;
} }
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>(); HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
if (!height) { if (!height) {
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -1067,7 +1067,7 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
if (colorSize == 0) { if (colorSize == 0) {
return STOP_RECURSION; return STOP_RECURSION;
} }
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>(); HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
if (!color) { if (!color) {
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -1149,14 +1149,14 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
return DEFAULT_ORDER; return DEFAULT_ORDER;
} }
HeightfieldBuffer* buffer = NULL; HeightfieldBuffer* buffer = NULL;
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>(); HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
if (height) { if (height) {
const QByteArray& heightContents = height->getContents(); const QByteArray& heightContents = height->getContents();
int size = glm::sqrt(heightContents.size()); int size = glm::sqrt(heightContents.size());
int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION; int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION;
int heightContentsSize = extendedSize * extendedSize; int heightContentsSize = extendedSize * extendedSize;
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>(); HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
int colorContentsSize = 0; int colorContentsSize = 0;
if (color) { if (color) {
const QByteArray& colorContents = color->getContents(); const QByteArray& colorContents = color->getContents();

View file

@ -957,14 +957,28 @@ void ImportHeightfieldTool::apply() {
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data()); HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
MetavoxelData data; MetavoxelData data;
data.setSize(scale); 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( data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
if (!buffer->getColor().isEmpty()) {
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor())); QByteArray color;
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( if (buffer->getColor().isEmpty()) {
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); 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( MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale, data)) }; _translation->getValue() + buffer->getTranslation() * scale, data)) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true); Application::getInstance()->getMetavoxels()->applyEdit(message, true);
@ -1165,20 +1179,20 @@ QVariant HeightfieldColorBrushTool::createEdit(bool alternate) {
HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) : HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Texture Brush") { HeightfieldBrushTool(editor, "Texture Brush") {
_form->addRow("URL:", _url = new QUrlEditor(this)); _form->addRow(_textureEditor = new SharedObjectEditor(&HeightfieldTexture::staticMetaObject, false));
_url->setURL(QUrl()); connect(_textureEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldTextureBrushTool::updateTexture);
connect(_url, &QUrlEditor::urlChanged, this, &HeightfieldTextureBrushTool::updateTexture);
} }
QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) { QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) {
if (alternate) { if (alternate) {
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), QUrl(), QColor())); return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
} else { } else {
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), _url->getURL(), return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), _textureEditor->getObject(),
_texture ? _texture->getAverageColor() : QColor())); _texture ? _texture->getAverageColor() : QColor()));
} }
} }
void HeightfieldTextureBrushTool::updateTexture() { void HeightfieldTextureBrushTool::updateTexture() {
_texture = Application::getInstance()->getTextureCache()->getTexture(_url->getURL()); HeightfieldTexture* texture = static_cast<HeightfieldTexture*>(_textureEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL());
} }

View file

@ -26,9 +26,9 @@ class QListWidget;
class QPushButton; class QPushButton;
class QScrollArea; class QScrollArea;
class QSpinBox; class QSpinBox;
class QUrlEditor;
class MetavoxelTool; class MetavoxelTool;
class SharedObjectEditor;
class Vec3Editor; class Vec3Editor;
/// Allows editing metavoxels. /// Allows editing metavoxels.
@ -380,7 +380,7 @@ private slots:
private: private:
QUrlEditor* _url; SharedObjectEditor* _textureEditor;
QSharedPointer<NetworkTexture> _texture; QSharedPointer<NetworkTexture> _texture;
}; };

View file

@ -23,6 +23,7 @@ REGISTER_META_OBJECT(QRgbAttribute)
REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(PackedNormalAttribute)
REGISTER_META_OBJECT(SpannerQRgbAttribute) REGISTER_META_OBJECT(SpannerQRgbAttribute)
REGISTER_META_OBJECT(SpannerPackedNormalAttribute) REGISTER_META_OBJECT(SpannerPackedNormalAttribute)
REGISTER_META_OBJECT(HeightfieldTexture)
REGISTER_META_OBJECT(HeightfieldAttribute) REGISTER_META_OBJECT(HeightfieldAttribute)
REGISTER_META_OBJECT(HeightfieldColorAttribute) REGISTER_META_OBJECT(HeightfieldColorAttribute)
REGISTER_META_OBJECT(HeightfieldTextureAttribute) REGISTER_META_OBJECT(HeightfieldTextureAttribute)
@ -502,8 +503,7 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) :
_contents(contents) { _contents(contents) {
} }
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { HeightfieldData::~HeightfieldData() {
read(in, bytes, color);
} }
enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; 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) { if (!reference) {
read(in, bytes, color); read(in, bytes);
return; return;
} }
QMutexLocker locker(&reference->_encodedDeltaMutex); QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->_encodedDelta = in.readAligned(bytes); reference->setEncodedDelta(in.readAligned(bytes));
reference->_deltaData = this; reference->setDeltaData(HeightfieldDataPointer(this));
_contents = reference->_contents; _contents = reference->getContents();
QImage image = decodeHeightfieldImage(reference->_encodedDelta); QImage image = decodeHeightfieldImage(reference->getEncodedDelta());
if (image.isNull()) { if (image.isNull()) {
return; return;
} }
QPoint offset = image.offset(); QPoint offset = image.offset();
image = image.convertToFormat(QImage::Format_RGB888); image = image.convertToFormat(QImage::Format_RGB888);
if (offset.x() == 0) { if (offset.x() == 0) {
set(image, color); set(image);
return; return;
} }
int minX = offset.x() - 1; int minX = offset.x() - 1;
int minY = offset.y() - 1; int minY = offset.y() - 1;
if (color) { int size = glm::sqrt((float)_contents.size());
int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); char* lineDest = _contents.data() + minY * size + minX;
char* dest = _contents.data() + (minY * size + minX) * COLOR_BYTES; for (int y = 0; y < image.height(); y++) {
int destStride = size * COLOR_BYTES; const uchar* src = image.constScanLine(y);
int srcStride = image.width() * COLOR_BYTES; for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += COLOR_BYTES) {
for (int y = 0; y < image.height(); y++) { *dest = *src;
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;
} }
lineDest += size;
} }
} }
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& ancestor, HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor,
const glm::vec3& minimum, float size, bool color) { const glm::vec3& minimum, float size) {
QMutexLocker locker(&_encodedSubdivisionsMutex); QMutexLocker locker(&_encodedSubdivisionsMutex);
int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1;
if (_encodedSubdivisions.size() <= index) { if (_encodedSubdivisions.size() <= index) {
@ -610,61 +607,35 @@ HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldData
int destSize = image.width(); int destSize = image.width();
const uchar* src = image.constBits(); const uchar* src = image.constBits();
const QByteArray& ancestorContents = ancestor->getContents(); 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); int ancestorSize = glm::sqrt((float)ancestorContents.size());
char* dest = _contents.data(); float ancestorY = minimum.z * ancestorSize;
int stride = image.width() * COLOR_BYTES; float ancestorIncrement = size * ancestorSize / destSize;
for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { _contents = QByteArray(destSize * destSize, 0);
const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; char* dest = _contents.data();
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); for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) {
char* dest = _contents.data(); const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize;
float ancestorX = minimum.x * ancestorSize;
for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { for (char* end = dest + destSize; dest != end; src += COLOR_BYTES, ancestorX += ancestorIncrement) {
const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; const uchar* ref = lineRef + (int)ancestorX;
float ancestorX = minimum.x * ancestorSize; *dest++ = *ref++ + *src;
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); QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) { if (_encoded.isEmpty()) {
QImage image; QImage image;
if (color) { int size = glm::sqrt((float)_contents.size());
int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); image = QImage(size, size, QImage::Format_RGB888);
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); uchar* dest = image.bits();
} else { for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) {
int size = glm::sqrt((float)_contents.size()); *dest++ = *src;
image = QImage(size, size, QImage::Format_RGB888); *dest++ = *src;
uchar* dest = image.bits(); *dest++ = *src;
for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) {
*dest++ = *src;
*dest++ = *src;
*dest++ = *src;
}
} }
_encoded = encodeHeightfieldImage(image); _encoded = encodeHeightfieldImage(image);
} }
@ -672,95 +643,58 @@ void HeightfieldData::write(Bitstream& out, bool color) {
out.writeAligned(_encoded); 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()) { if (!reference || reference->getContents().size() != _contents.size()) {
write(out, color); write(out);
return; return;
} }
QMutexLocker locker(&reference->_encodedDeltaMutex); QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->_encodedDelta.isEmpty() || reference->_deltaData != this) { if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
QImage image; QImage image;
int minX, minY; int size = glm::sqrt((float)_contents.size());
if (color) { int minX = size, minY = size;
int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); int maxX = -1, maxY = -1;
minX = size; const char* src = _contents.constData();
minY = size; const char* ref = reference->getContents().constData();
int maxX = -1, maxY = -1; for (int y = 0; y < size; y++) {
const char* src = _contents.constData(); bool difference = false;
const char* ref = reference->_contents.constData(); for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) { if (*src++ != *ref++) {
bool difference = false; minX = qMin(minX, x);
for (int x = 0; x < size; x++, src += COLOR_BYTES, ref += COLOR_BYTES) { maxX = qMax(maxX, x);
if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { difference = true;
minX = qMin(minX, x);
maxX = qMax(maxX, x);
difference = true;
}
}
if (difference) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
} }
} }
if (maxX >= minX) { if (difference) {
int width = maxX - minX + 1; minY = qMin(minY, y);
int height = maxY - minY + 1; maxY = qMax(maxY, y);
image = QImage(width, height, QImage::Format_RGB888);
src = _contents.constData() + (minY * size + minX) * COLOR_BYTES;
int srcStride = size * COLOR_BYTES;
int destStride = width * COLOR_BYTES;
for (int y = 0; y < height; y++) {
memcpy(image.scanLine(y), src, destStride);
src += srcStride;
}
} }
} else { }
int size = glm::sqrt((float)_contents.size()); if (maxX >= minX) {
minX = size; int width = qMax(maxX - minX + 1, 0);
minY = size; int height = qMax(maxY - minY + 1, 0);
int maxX = -1, maxY = -1; image = QImage(width, height, QImage::Format_RGB888);
const char* src = _contents.constData(); const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX;
const char* ref = reference->_contents.constData(); for (int y = 0; y < height; y++) {
for (int y = 0; y < size; y++) { uchar* dest = image.scanLine(y);
bool difference = false; for (const uchar* src = lineSrc, *end = src + width; src != end; src++) {
for (int x = 0; x < size; x++) { *dest++ = *src;
if (*src++ != *ref++) { *dest++ = *src;
minX = qMin(minX, x); *dest++ = *src;
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;
} }
lineSrc += size;
} }
} }
image.setOffset(QPoint(minX + 1, minY + 1)); image.setOffset(QPoint(minX + 1, minY + 1));
reference->_encodedDelta = encodeHeightfieldImage(image); reference->setEncodedDelta(encodeHeightfieldImage(image));
reference->_deltaData = this; reference->setDeltaData(HeightfieldDataPointer(this));
} }
out << reference->_encodedDelta.size(); out << reference->getEncodedDelta().size();
out.writeAligned(reference->_encodedDelta); out.writeAligned(reference->getEncodedDelta());
} }
void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPointer& ancestor, void HeightfieldHeightData::writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor,
const glm::vec3& minimum, float size, bool color) { const glm::vec3& minimum, float size) {
QMutexLocker locker(&_encodedSubdivisionsMutex); QMutexLocker locker(&_encodedSubdivisionsMutex);
int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1;
if (_encodedSubdivisions.size() <= index) { if (_encodedSubdivisions.size() <= index) {
@ -771,46 +705,24 @@ void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPoint
QImage image; QImage image;
const QByteArray& ancestorContents = ancestor->getContents(); const QByteArray& ancestorContents = ancestor->getContents();
const uchar* src = (const uchar*)_contents.constData(); 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); int destSize = glm::sqrt((float)_contents.size());
float ancestorY = minimum.z * ancestorSize; image = QImage(destSize, destSize, QImage::Format_RGB888);
float ancestorIncrement = size * ancestorSize / destSize; uchar* dest = image.bits();
int ancestorStride = ancestorSize * COLOR_BYTES;
for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { int ancestorSize = glm::sqrt((float)ancestorContents.size());
const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; float ancestorY = minimum.z * ancestorSize;
float ancestorX = minimum.x * ancestorSize; float ancestorIncrement = size * ancestorSize / destSize;
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()); for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) {
float ancestorY = minimum.z * ancestorSize; const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize;
float ancestorIncrement = size * ancestorSize / destSize; float ancestorX = minimum.x * ancestorSize;
for (const uchar* end = src + destSize; src != end; ancestorX += ancestorIncrement) {
for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { const uchar* ref = lineRef + (int)ancestorX;
const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; uchar difference = *src++ - *ref;
float ancestorX = minimum.x * ancestorSize; *dest++ = difference;
for (const uchar* end = src + destSize; src != end; ancestorX += ancestorIncrement) { *dest++ = difference;
const uchar* ref = lineRef + (int)ancestorX; *dest++ = difference;
uchar difference = *src++ - *ref;
*dest++ = difference;
*dest++ = difference;
*dest++ = difference;
}
} }
} }
subdivision.data = encodeHeightfieldImage(image, true); subdivision.data = encodeHeightfieldImage(image, true);
@ -820,27 +732,211 @@ void HeightfieldData::writeSubdivided(Bitstream& out, const HeightfieldDataPoint
out.writeAligned(subdivision.data); out.writeAligned(subdivision.data);
} }
void HeightfieldData::read(Bitstream& in, int bytes, bool color) { void HeightfieldHeightData::read(Bitstream& in, int bytes) {
set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color); set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888));
} }
void HeightfieldData::set(const QImage& image, bool color) { void HeightfieldHeightData::set(const QImage& image) {
if (color) { _contents.resize(image.width() * image.height());
_contents.resize(image.width() * image.height() * COLOR_BYTES); char* dest = _contents.data();
memcpy(_contents.data(), image.constBits(), _contents.size()); for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES;
src != end; src += COLOR_BYTES) {
*dest++ = *src;
}
}
} else { HeightfieldColorData::HeightfieldColorData(const QByteArray& contents) :
_contents.resize(image.width() * image.height()); HeightfieldData(contents) {
char* dest = _contents.data(); }
for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES;
src != end; src += COLOR_BYTES) { HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes) {
*dest++ = *src; 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) : HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
InlineAttribute<HeightfieldDataPointer>(name) { InlineAttribute<HeightfieldHeightDataPointer>(name) {
} }
void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { 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; int size;
in >> size; in >> size;
if (size == 0) { if (size == 0) {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer();
} else { } 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) { if (!isLeaf) {
return; return;
} }
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value); HeightfieldHeightDataPointer data = decodeInline<HeightfieldHeightDataPointer>(value);
if (data) { if (data) {
data->write(out, false); data->write(out);
} else { } else {
out << 0; out << 0;
} }
@ -875,10 +971,10 @@ void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* referenc
int size; int size;
in >> size; in >> size;
if (size == 0) { if (size == 0) {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer();
} else { } else {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData( *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData(
in, size, decodeInline<HeightfieldDataPointer>(reference), false)); in, size, decodeInline<HeightfieldHeightDataPointer>(reference)));
} }
} }
@ -886,9 +982,9 @@ void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* referen
if (!isLeaf) { if (!isLeaf) {
return; return;
} }
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value); HeightfieldHeightDataPointer data = decodeInline<HeightfieldHeightDataPointer>(value);
if (data) { if (data) {
data->writeDelta(out, decodeInline<HeightfieldDataPointer>(reference), false); data->writeDelta(out, decodeInline<HeightfieldHeightDataPointer>(reference));
} else { } else {
out << 0; 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 { bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0; int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) { for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer pointer = decodeInline<HeightfieldDataPointer>(children[i]); HeightfieldHeightDataPointer pointer = decodeInline<HeightfieldHeightDataPointer>(children[i]);
if (pointer) { if (pointer) {
maxSize = qMax(maxSize, pointer->getContents().size()); maxSize = qMax(maxSize, pointer->getContents().size());
} }
} }
if (maxSize == 0) { if (maxSize == 0) {
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer();
return true; return true;
} }
int size = glm::sqrt((float)maxSize); int size = glm::sqrt((float)maxSize);
QByteArray contents(size * size, 0); QByteArray contents(size * size, 0);
int halfSize = size / 2; int halfSize = size / 2;
for (int i = 0; i < MERGE_COUNT; i++) { for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]); HeightfieldHeightDataPointer child = decodeInline<HeightfieldHeightDataPointer>(children[i]);
if (!child) { if (!child) {
continue; continue;
} }
@ -920,7 +1016,7 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead)
int xIndex = i & INDEX_MASK; int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1; const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK; int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
if (yIndex == 0 && decodeInline<HeightfieldDataPointer>(children[i | (1 << Y_SHIFT)])) { if (yIndex == 0 && decodeInline<HeightfieldHeightDataPointer>(children[i | (1 << Y_SHIFT)])) {
continue; // bottom is overriden by top continue; // bottom is overriden by top
} }
const int HALF_RANGE = 128; 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; return false;
} }
HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
InlineAttribute<HeightfieldDataPointer>(name) { InlineAttribute<HeightfieldColorDataPointer>(name) {
} }
void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { 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; int size;
in >> size; in >> size;
if (size == 0) { if (size == 0) {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer();
} else { } 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) { if (!isLeaf) {
return; return;
} }
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value); HeightfieldColorDataPointer data = decodeInline<HeightfieldColorDataPointer>(value);
if (data) { if (data) {
data->write(out, true); data->write(out);
} else { } else {
out << 0; out << 0;
} }
@ -999,10 +1095,10 @@ void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* ref
int size; int size;
in >> size; in >> size;
if (size == 0) { if (size == 0) {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(); *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer();
} else { } else {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData( *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData(
in, size, decodeInline<HeightfieldDataPointer>(reference), true)); in, size, decodeInline<HeightfieldColorDataPointer>(reference)));
} }
} }
@ -1010,9 +1106,9 @@ void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* re
if (!isLeaf) { if (!isLeaf) {
return; return;
} }
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value); HeightfieldColorDataPointer data = decodeInline<HeightfieldColorDataPointer>(value);
if (data) { if (data) {
data->writeDelta(out, decodeInline<HeightfieldDataPointer>(reference), true); data->writeDelta(out, decodeInline<HeightfieldColorDataPointer>(reference));
} else { } else {
out << 0; 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 { bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0; int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) { for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer pointer = decodeInline<HeightfieldDataPointer>(children[i]); HeightfieldColorDataPointer pointer = decodeInline<HeightfieldColorDataPointer>(children[i]);
if (pointer) { if (pointer) {
maxSize = qMax(maxSize, pointer->getContents().size()); maxSize = qMax(maxSize, pointer->getContents().size());
} }
} }
if (maxSize == 0) { if (maxSize == 0) {
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer();
return true; return true;
} }
int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES); int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES);
QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0); QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0);
int halfSize = size / 2; int halfSize = size / 2;
for (int i = 0; i < MERGE_COUNT; i++) { for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]); HeightfieldColorDataPointer child = decodeInline<HeightfieldColorDataPointer>(children[i]);
if (!child) { if (!child) {
continue; continue;
} }
@ -1044,7 +1140,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
int xIndex = i & INDEX_MASK; int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1; const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK; int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
if (yIndex == 0 && decodeInline<HeightfieldDataPointer>(children[i | (1 << Y_SHIFT)])) { if (yIndex == 0 && decodeInline<HeightfieldColorDataPointer>(children[i | (1 << Y_SHIFT)])) {
continue; // bottom is overriden by top continue; // bottom is overriden by top
} }
int Z_SHIFT = 2; 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; return false;
} }

View file

@ -18,6 +18,7 @@
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QSharedPointer> #include <QSharedPointer>
#include <QString> #include <QString>
#include <QUrl>
#include <QWidget> #include <QWidget>
#include "Bitstream.h" #include "Bitstream.h"
@ -28,7 +29,9 @@ class QScriptEngine;
class QScriptValue; class QScriptValue;
class Attribute; class Attribute;
class HeightfieldColorData;
class HeightfieldData; class HeightfieldData;
class HeightfieldHeightData;
class MetavoxelData; class MetavoxelData;
class MetavoxelLOD; class MetavoxelLOD;
class MetavoxelNode; class MetavoxelNode;
@ -439,23 +442,20 @@ public:
static const int COLOR_BYTES = 3; static const int COLOR_BYTES = 3;
HeightfieldData(const QByteArray& contents); HeightfieldData(const QByteArray& contents = QByteArray());
HeightfieldData(Bitstream& in, int bytes, bool color); virtual ~HeightfieldData();
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; } const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out, bool color); void setDeltaData(const HeightfieldDataPointer& deltaData) { _deltaData = deltaData; }
void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color); const HeightfieldDataPointer& getDeltaData() const { return _deltaData; }
void writeSubdivided(Bitstream& out, const HeightfieldDataPointer& ancestor,
const glm::vec3& minimum, float size, bool color);
private: void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
void read(Bitstream& in, int bytes, bool color); QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
void set(const QImage& image, bool color);
protected:
QByteArray _contents; QByteArray _contents;
QByteArray _encoded; QByteArray _encoded;
@ -474,8 +474,70 @@ private:
QMutex _encodedSubdivisionsMutex; QMutex _encodedSubdivisionsMutex;
}; };
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> 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<HeightfieldColorData> 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. /// An attribute that stores heightfield data.
class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> { class HeightfieldAttribute : public InlineAttribute<HeightfieldHeightDataPointer> {
Q_OBJECT Q_OBJECT
public: public:
@ -492,7 +554,7 @@ public:
}; };
/// An attribute that stores heightfield colors. /// An attribute that stores heightfield colors.
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldDataPointer> { class HeightfieldColorAttribute : public InlineAttribute<HeightfieldColorDataPointer> {
Q_OBJECT Q_OBJECT
public: public:

View file

@ -354,7 +354,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) { if (!info.isLeaf) {
return DEFAULT_ORDER; return DEFAULT_ORDER;
} }
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>(); HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
if (!pointer) { if (!pointer) {
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -396,8 +396,8 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
lineDest += size; lineDest += size;
} }
if (changed) { if (changed) {
HeightfieldDataPointer newPointer(new HeightfieldData(contents)); HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer)); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldHeightDataPointer>(newPointer));
} }
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -442,7 +442,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) { if (!info.isLeaf) {
return DEFAULT_ORDER; return DEFAULT_ORDER;
} }
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>(); HeightfieldColorDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
if (!pointer) { if (!pointer) {
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -481,8 +481,8 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
lineDest += stride; lineDest += stride;
} }
if (changed) { if (changed) {
HeightfieldDataPointer newPointer(new HeightfieldData(contents)); HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer)); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldColorDataPointer>(newPointer));
} }
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -493,10 +493,10 @@ void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjec
} }
PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius, PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius,
const QUrl& url, const QColor& averageColor) : const SharedObjectPointer& texture, const QColor& averageColor) :
position(position), position(position),
radius(radius), radius(radius),
url(url), texture(texture),
averageColor(averageColor) { averageColor(averageColor) {
} }
@ -531,7 +531,7 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) { if (!info.isLeaf) {
return DEFAULT_ORDER; return DEFAULT_ORDER;
} }
HeightfieldDataPointer pointer = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>(); HeightfieldColorDataPointer pointer = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
if (!pointer) { if (!pointer) {
return STOP_RECURSION; return STOP_RECURSION;
} }
@ -570,8 +570,8 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) {
lineDest += stride; lineDest += stride;
} }
if (changed) { if (changed) {
HeightfieldDataPointer newPointer(new HeightfieldData(contents)); HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldDataPointer>(newPointer)); info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldColorDataPointer>(newPointer));
} }
return STOP_RECURSION; return STOP_RECURSION;
} }

View file

@ -249,11 +249,11 @@ public:
STREAM glm::vec3 position; STREAM glm::vec3 position;
STREAM float radius; STREAM float radius;
STREAM QUrl url; STREAM SharedObjectPointer texture;
STREAM QColor averageColor; STREAM QColor averageColor;
PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const QUrl& url = QUrl(), PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const QColor& averageColor = QColor()); const SharedObjectPointer& texture = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
}; };

View file

@ -158,7 +158,7 @@ SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nulla
_type->addItem("(none)"); _type->addItem("(none)");
} }
foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) {
// add add constructable subclasses // add constructable subclasses
if (metaObject->constructorCount() > 0) { if (metaObject->constructorCount() > 0) {
_type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); _type->addItem(metaObject->className(), QVariant::fromValue(metaObject));
} }
@ -226,6 +226,7 @@ void SharedObjectEditor::updateType() {
const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value<const QMetaObject*>(); const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value<const QMetaObject*>();
if (!metaObject) { if (!metaObject) {
_object.reset(); _object.reset();
emit objectChanged(_object);
return; return;
} }
QObject* newObject = metaObject->newInstance(); QObject* newObject = metaObject->newInstance();
@ -259,7 +260,7 @@ void SharedObjectEditor::updateType() {
} }
} }
} }
_object = static_cast<SharedObject*>(newObject); emit objectChanged(_object = static_cast<SharedObject*>(newObject));
} }
void SharedObjectEditor::propertyChanged() { void SharedObjectEditor::propertyChanged() {
@ -275,6 +276,7 @@ void SharedObjectEditor::propertyChanged() {
QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType());
property.write(object, widget->property(valuePropertyName)); property.write(object, widget->property(valuePropertyName));
} }
emit objectChanged(_object);
} }
void SharedObjectEditor::updateProperty() { void SharedObjectEditor::updateProperty() {

View file

@ -211,7 +211,7 @@ Q_DECLARE_METATYPE(SharedObjectSet)
/// Allows editing shared object instances. /// Allows editing shared object instances.
class SharedObjectEditor : public QWidget { class SharedObjectEditor : public QWidget {
Q_OBJECT 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: public:
@ -222,6 +222,10 @@ public:
/// "Detaches" the object pointer, copying it if anyone else is holding a reference. /// "Detaches" the object pointer, copying it if anyone else is holding a reference.
void detachObject(); void detachObject();
signals:
void objectChanged(const SharedObjectPointer& object);
public slots: public slots:
void setObject(const SharedObjectPointer& object); void setObject(const SharedObjectPointer& object);