mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-10 20:47:22 +02:00
More chipping away at heightfield spanners.
This commit is contained in:
parent
08475cc90b
commit
16bf2a3ad3
3 changed files with 208 additions and 7 deletions
|
@ -329,7 +329,7 @@ static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = fa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QImage decodeHeightfieldImage(const QByteArray& data) {
|
static QImage decodeHeightfieldImage(const QByteArray& data) {
|
||||||
switch (data.at(0)) {
|
switch (data.at(0)) {
|
||||||
case NULL_HEIGHTFIELD_IMAGE:
|
case NULL_HEIGHTFIELD_IMAGE:
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QItemEditorFactory>
|
#include <QItemEditorFactory>
|
||||||
|
@ -609,9 +610,19 @@ HeightfieldHeight::HeightfieldHeight(int width, const QVector<quint16>& contents
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) {
|
HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) {
|
||||||
|
read(in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) {
|
HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) {
|
||||||
|
if (!reference) {
|
||||||
|
read(in, bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||||
|
reference->setEncodedDelta(in.readAligned(bytes));
|
||||||
|
reference->setDeltaData(DataBlockPointer(this));
|
||||||
|
_width = reference->getWidth();
|
||||||
|
_contents = reference->getContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldHeight::write(Bitstream& out) {
|
void HeightfieldHeight::write(Bitstream& out) {
|
||||||
|
@ -624,6 +635,19 @@ void HeightfieldHeight::write(Bitstream& out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) {
|
void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) {
|
||||||
|
if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) {
|
||||||
|
write(out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||||
|
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||||
|
|
||||||
|
}
|
||||||
|
out << reference->getEncodedDelta().size();
|
||||||
|
out.writeAligned(reference->getEncodedDelta());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeightfieldHeight::read(Bitstream& in, int bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) {
|
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) {
|
||||||
|
@ -641,15 +665,27 @@ Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value) {
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
value = HeightfieldHeightPointer();
|
value = HeightfieldHeightPointer();
|
||||||
} else {
|
} else {
|
||||||
value = new HeightfieldHeight(0, QVector<quint16>());
|
value = new HeightfieldHeight(in, size);
|
||||||
}
|
}
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
|
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
|
||||||
|
if (value) {
|
||||||
|
value->writeDelta(*this, reference);
|
||||||
|
} else {
|
||||||
|
*this << 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
|
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
|
||||||
|
int size;
|
||||||
|
*this >> size;
|
||||||
|
if (size == 0) {
|
||||||
|
value = HeightfieldHeightPointer();
|
||||||
|
} else {
|
||||||
|
value = new HeightfieldHeight(*this, size, reference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) :
|
HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) :
|
||||||
|
@ -745,27 +781,95 @@ void HeightfieldHeightEditor::clear() {
|
||||||
_clear->setEnabled(false);
|
_clear->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_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 && !lossless) {
|
||||||
|
qint32 offsetX = image.offset().x(), offsetY = image.offset().y();
|
||||||
|
buffer.write((char*)&offsetX, sizeof(qint32));
|
||||||
|
buffer.write((char*)&offsetY, sizeof(qint32));
|
||||||
|
image.save(&buffer, "JPG");
|
||||||
|
return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE);
|
||||||
|
image.save(&buffer, "PNG");
|
||||||
|
return buffer.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QImage decodeHeightfieldImage(const QByteArray& data) {
|
||||||
|
switch (data.at(0)) {
|
||||||
|
case NULL_HEIGHTFIELD_IMAGE:
|
||||||
|
default:
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
case NORMAL_HEIGHTFIELD_IMAGE:
|
||||||
|
return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1));
|
||||||
|
|
||||||
|
case DEFLATED_HEIGHTFIELD_IMAGE: {
|
||||||
|
QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1);
|
||||||
|
const int OFFSET_SIZE = sizeof(qint32) * 2;
|
||||||
|
QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE);
|
||||||
|
const qint32* offsets = (const qint32*)inflated.constData();
|
||||||
|
image.setOffset(QPoint(offsets[0], offsets[1]));
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) :
|
HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) :
|
||||||
HeightfieldData(width),
|
HeightfieldData(width),
|
||||||
_contents(contents) {
|
_contents(contents) {
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) {
|
HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) {
|
||||||
|
read(in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) {
|
HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) {
|
||||||
|
if (!reference) {
|
||||||
|
read(in, bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||||
|
reference->setEncodedDelta(in.readAligned(bytes));
|
||||||
|
reference->setDeltaData(DataBlockPointer(this));
|
||||||
|
_width = reference->getWidth();
|
||||||
|
_contents = reference->getContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldColor::write(Bitstream& out) {
|
void HeightfieldColor::write(Bitstream& out) {
|
||||||
QMutexLocker locker(&_encodedMutex);
|
QMutexLocker locker(&_encodedMutex);
|
||||||
if (_encoded.isEmpty()) {
|
if (_encoded.isEmpty()) {
|
||||||
|
QImage image;
|
||||||
|
image = QImage((uchar*)_contents.data(), _width, _contents.size() / (_width * COLOR_BYTES), QImage::Format_RGB888);
|
||||||
|
_encoded = encodeHeightfieldImage(image);
|
||||||
}
|
}
|
||||||
out << _encoded.size();
|
out << _encoded.size();
|
||||||
out.writeAligned(_encoded);
|
out.writeAligned(_encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) {
|
void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) {
|
||||||
|
if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) {
|
||||||
|
write(out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||||
|
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||||
|
|
||||||
|
}
|
||||||
|
out << reference->getEncodedDelta().size();
|
||||||
|
out.writeAligned(reference->getEncodedDelta());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeightfieldColor::read(Bitstream& in, int bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) {
|
Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) {
|
||||||
|
@ -783,16 +887,27 @@ Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value) {
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
value = HeightfieldColorPointer();
|
value = HeightfieldColorPointer();
|
||||||
} else {
|
} else {
|
||||||
value = new HeightfieldColor(0, QByteArray());
|
value = new HeightfieldColor(in, size);
|
||||||
}
|
}
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
|
template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
|
||||||
|
if (value) {
|
||||||
|
value->writeDelta(*this, reference);
|
||||||
|
} else {
|
||||||
|
*this << 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
|
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
|
||||||
|
int size;
|
||||||
|
*this >> size;
|
||||||
|
if (size == 0) {
|
||||||
|
value = HeightfieldColorPointer();
|
||||||
|
} else {
|
||||||
|
value = new HeightfieldColor(*this, size, reference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) :
|
HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) :
|
||||||
|
@ -847,6 +962,29 @@ void HeightfieldColorEditor::clear() {
|
||||||
_clear->setEnabled(false);
|
_clear->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int HEIGHTFIELD_MATERIAL_HEADER_SIZE = sizeof(qint32) * 4;
|
||||||
|
|
||||||
|
static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) {
|
||||||
|
QByteArray inflated(HEIGHTFIELD_MATERIAL_HEADER_SIZE, 0);
|
||||||
|
qint32* header = (qint32*)inflated.data();
|
||||||
|
*header++ = offsetX;
|
||||||
|
*header++ = offsetY;
|
||||||
|
*header++ = width;
|
||||||
|
*header++ = height;
|
||||||
|
inflated.append(contents);
|
||||||
|
return qCompress(inflated);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray decodeHeightfieldMaterial(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(HEIGHTFIELD_MATERIAL_HEADER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents,
|
HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents,
|
||||||
const QVector<SharedObjectPointer>& materials) :
|
const QVector<SharedObjectPointer>& materials) :
|
||||||
HeightfieldData(width),
|
HeightfieldData(width),
|
||||||
|
@ -855,15 +993,43 @@ HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents,
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) {
|
HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) {
|
||||||
|
read(in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) {
|
HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) {
|
||||||
|
if (!reference) {
|
||||||
|
read(in, bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||||
|
reference->setEncodedDelta(in.readAligned(bytes));
|
||||||
|
in.readDelta(_materials, reference->getMaterials());
|
||||||
|
reference->setDeltaData(DataBlockPointer(this));
|
||||||
|
_width = reference->getWidth();
|
||||||
|
_contents = reference->getContents();
|
||||||
|
|
||||||
|
int offsetX, offsetY, width, height;
|
||||||
|
QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height);
|
||||||
|
if (delta.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (offsetX == 0) {
|
||||||
|
_contents = delta;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int minX = offsetX - 1;
|
||||||
|
int minY = offsetY - 1;
|
||||||
|
const char* src = delta.constData();
|
||||||
|
char* dest = _contents.data() + minY * _width + minX;
|
||||||
|
for (int y = 0; y < height; y++, src += width, dest += _width) {
|
||||||
|
memcpy(dest, src, width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldMaterial::write(Bitstream& out) {
|
void HeightfieldMaterial::write(Bitstream& out) {
|
||||||
QMutexLocker locker(&_encodedMutex);
|
QMutexLocker locker(&_encodedMutex);
|
||||||
if (_encoded.isEmpty()) {
|
if (_encoded.isEmpty()) {
|
||||||
|
_encoded = encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents);
|
||||||
}
|
}
|
||||||
out << _encoded.size();
|
out << _encoded.size();
|
||||||
out.writeAligned(_encoded);
|
out.writeAligned(_encoded);
|
||||||
|
@ -871,6 +1037,23 @@ void HeightfieldMaterial::write(Bitstream& out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) {
|
void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) {
|
||||||
|
if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) {
|
||||||
|
write(out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||||
|
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||||
|
|
||||||
|
}
|
||||||
|
out << reference->getEncodedDelta().size();
|
||||||
|
out.writeAligned(reference->getEncodedDelta());
|
||||||
|
out.writeDelta(_materials, reference->getMaterials());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeightfieldMaterial::read(Bitstream& in, int bytes) {
|
||||||
|
int offsetX, offsetY, height;
|
||||||
|
_contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height);
|
||||||
|
in >> _materials;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value) {
|
Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value) {
|
||||||
|
@ -888,16 +1071,28 @@ Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value) {
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
value = HeightfieldMaterialPointer();
|
value = HeightfieldMaterialPointer();
|
||||||
} else {
|
} else {
|
||||||
value = new HeightfieldMaterial(0, QByteArray(), QVector<SharedObjectPointer>());
|
value = new HeightfieldMaterial(in, size);
|
||||||
}
|
}
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value,
|
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value,
|
||||||
const HeightfieldMaterialPointer& reference) {
|
const HeightfieldMaterialPointer& reference) {
|
||||||
|
if (value) {
|
||||||
|
value->writeDelta(*this, reference);
|
||||||
|
} else {
|
||||||
|
*this << 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) {
|
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) {
|
||||||
|
int size;
|
||||||
|
*this >> size;
|
||||||
|
if (size == 0) {
|
||||||
|
value = HeightfieldMaterialPointer();
|
||||||
|
} else {
|
||||||
|
value = new HeightfieldMaterial(*this, size, reference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Heightfield::Heightfield() :
|
Heightfield::Heightfield() :
|
||||||
|
|
|
@ -334,6 +334,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void read(Bitstream& in, int bytes);
|
||||||
|
|
||||||
QVector<quint16> _contents;
|
QVector<quint16> _contents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -392,6 +394,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void read(Bitstream& in, int bytes);
|
||||||
|
|
||||||
QByteArray _contents;
|
QByteArray _contents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -451,6 +455,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void read(Bitstream& in, int bytes);
|
||||||
|
|
||||||
QByteArray _contents;
|
QByteArray _contents;
|
||||||
QVector<SharedObjectPointer> _materials;
|
QVector<SharedObjectPointer> _materials;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue