Working on heightfield transmission, merging.

This commit is contained in:
Andrzej Kapolka 2014-07-30 14:54:38 -07:00
parent ff01d4b80d
commit 82e442c115
6 changed files with 220 additions and 32 deletions

View file

@ -29,6 +29,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation)
REGISTER_META_OBJECT(SphereRenderer)
REGISTER_META_OBJECT(StaticModelRenderer)
static int texturePointerMetaTypeId = qRegisterMetaType<TexturePointer>();
static int bufferPointVectorMetaTypeId = qRegisterMetaType<BufferPointVector>();
void MetavoxelSystem::init() {
@ -112,6 +113,11 @@ void MetavoxelSystem::render() {
guideToAugmented(renderVisitor);
}
void MetavoxelSystem::deleteTextures(const TexturePointer& height, const TexturePointer& color) {
delete height;
delete color;
}
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) {
return new MetavoxelSystemClient(node, _updater);
}
@ -261,8 +267,19 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
_height(height),
_color(color),
_clearAfterLoading(clearAfterLoading),
_heightTexture(QOpenGLTexture::Target2D),
_colorTexture(QOpenGLTexture::Target2D) {
_heightTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)),
_colorTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)) {
}
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(const TexturePointer&, _heightTexture), Q_ARG(const TexturePointer&, _colorTexture));
} else {
delete _heightTexture;
delete _colorTexture;
}
}
class HeightfieldPoint {
@ -273,39 +290,39 @@ public:
void HeightfieldBuffer::render() {
// initialize textures, etc. on first render
if (!_heightTexture.isCreated()) {
if (!_heightTexture->isCreated()) {
int heightSize = glm::sqrt(_height.size());
_heightTexture.setSize(heightSize, heightSize);
_heightTexture.setAutoMipMapGenerationEnabled(false);
_heightTexture.setMinificationFilter(QOpenGLTexture::Linear);
_heightTexture.setWrapMode(QOpenGLTexture::ClampToEdge);
_heightTexture.setFormat(QOpenGLTexture::LuminanceFormat);
_heightTexture.allocateStorage();
_heightTexture.setData(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, _height.data());
_heightTexture->setSize(heightSize, heightSize);
_heightTexture->setAutoMipMapGenerationEnabled(false);
_heightTexture->setMinificationFilter(QOpenGLTexture::Linear);
_heightTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
_heightTexture->setFormat(QOpenGLTexture::LuminanceFormat);
_heightTexture->allocateStorage();
_heightTexture->setData(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, _height.data());
if (_clearAfterLoading) {
_height.clear();
}
if (!_color.isEmpty()) {
int colorSize = glm::sqrt(_color.size() / 3);
_colorTexture.setSize(colorSize, colorSize);
_colorTexture->setSize(colorSize, colorSize);
}
_colorTexture.setAutoMipMapGenerationEnabled(false);
_colorTexture.setMinificationFilter(QOpenGLTexture::Linear);
_colorTexture.setWrapMode(QOpenGLTexture::ClampToEdge);
_colorTexture.setFormat(QOpenGLTexture::RGBFormat);
_colorTexture.allocateStorage();
_colorTexture->setAutoMipMapGenerationEnabled(false);
_colorTexture->setMinificationFilter(QOpenGLTexture::Linear);
_colorTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
_colorTexture->setFormat(QOpenGLTexture::RGBFormat);
_colorTexture->allocateStorage();
if (!_color.isEmpty()) {
_colorTexture.setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, _color.data());
_colorTexture->setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, _color.data());
if (_clearAfterLoading) {
_color.clear();
}
} else {
const quint8 WHITE_COLOR[] = { 255, 255, 255 };
_colorTexture.setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, const_cast<quint8*>(WHITE_COLOR));
_colorTexture->setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, const_cast<quint8*>(WHITE_COLOR));
}
}
// create the buffer objects lazily
int size = _heightTexture.width();
int size = _heightTexture->width();
int sizeWithSkirt = size + 2;
int vertexCount = sizeWithSkirt * sizeWithSkirt;
int rows = sizeWithSkirt - 1;
@ -363,13 +380,13 @@ void HeightfieldBuffer::render() {
glTranslatef(_translation.x, _translation.y, _translation.z);
glScalef(_scale, _scale, _scale);
_heightTexture.bind(0);
_colorTexture.bind(1);
_heightTexture->bind(0);
_colorTexture->bind(1);
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
_colorTexture.release(1);
_heightTexture.release(0);
_colorTexture->release(1);
_heightTexture->release(0);
glPopMatrix();
@ -678,7 +695,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
_pointProgram.release();
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
_heightfieldProgram.bind();

View file

@ -26,6 +26,8 @@
class Model;
typedef QOpenGLTexture* TexturePointer;
/// Renders a metavoxel tree.
class MetavoxelSystem : public MetavoxelClientManager {
Q_OBJECT
@ -42,6 +44,8 @@ public:
void simulate(float deltaTime);
void render();
Q_INVOKABLE void deleteTextures(const TexturePointer& height, const TexturePointer& color);
protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
@ -57,6 +61,8 @@ private:
QReadWriteLock _lodLock;
};
Q_DECLARE_METATYPE(TexturePointer)
/// Describes contents of a point in a point buffer.
class BufferPoint {
public:
@ -129,6 +135,7 @@ public:
/// \param clearAfterLoading if true, clear the data arrays after we load them into textures in order to reclaim the space
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color,
bool clearAfterLoading = true);
~HeightfieldBuffer();
const glm::vec3& getTranslation() const { return _translation; }
@ -144,8 +151,8 @@ private:
QByteArray _height;
QByteArray _color;
bool _clearAfterLoading;
QOpenGLTexture _heightTexture;
QOpenGLTexture _colorTexture;
TexturePointer _heightTexture;
TexturePointer _colorTexture;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<int, BufferPair> _bufferPairs;

View file

@ -51,9 +51,13 @@ AttributeRegistry::AttributeRegistry() :
_heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))),
_heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))) {
// our baseline LOD threshold is for voxels; spanners are a different story
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f;
_heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
_heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
}
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
@ -461,16 +465,35 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) :
_contents(contents) {
}
const int BYTES_PER_PIXEL = 3;
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) :
_encoded(in.readAligned(bytes)) {
QImage image = QImage::fromData(_encoded).convertToFormat(QImage::Format_RGB888);
if (color) {
_contents.resize(image.width() * image.height() * BYTES_PER_PIXEL);
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() * BYTES_PER_PIXEL;
src != end; src += BYTES_PER_PIXEL) {
*dest++ = *src;
}
}
}
void HeightfieldData::write(Bitstream& out, bool color) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
QImage image;
const int BYTES_PER_PIXEL = 3;
QImage image;
if (color) {
int size = glm::sqrt(_contents.size() / (double)BYTES_PER_PIXEL);
int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL);
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888);
} else {
int size = glm::sqrt((double)_contents.size());
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++) {
@ -483,7 +506,8 @@ void HeightfieldData::write(Bitstream& out, bool color) {
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "JPG");
}
out << _encoded.size();
out.writeAligned(_encoded);
}
HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
@ -492,6 +516,13 @@ HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (isLeaf) {
int size;
in >> size;
if (size == 0) {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
} else {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
}
}
}
@ -500,11 +531,65 @@ void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
if (data) {
data->write(out, false);
} else {
out << 0;
}
}
}
bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer pointer = decodeInline<HeightfieldDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getContents().size());
}
}
if (maxSize == 0) {
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
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<HeightfieldDataPointer>(children[i]);
if (!child) {
continue;
}
const QByteArray& childContents = child->getContents();
int childSize = glm::sqrt((float)childContents.size());
if (childSize != size) {
continue; // TODO: handle differently-sized children
}
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
if (yIndex == 0 && decodeInline<HeightfieldDataPointer>(children[i | (1 << Y_SHIFT)])) {
continue; // bottom is overriden by top
}
const int HALF_RANGE = 128;
int yOffset = yIndex * HALF_RANGE;
int Z_SHIFT = 2;
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
char* dest = contents.data() + (zIndex * halfSize * size) + (xIndex * halfSize);
uchar* src0 = (uchar*)childContents.data();
uchar* src1 = src0 + 1;
uchar* src2 = src0 + childSize;
uchar* src3 = src2 + 1;
for (int z = 0; z < halfSize; z++) {
for (char* end = dest + halfSize; dest != end; ) {
*dest++ = yOffset + (qMax(qMax(*src0++, *src1++), qMax(*src2++, *src3++)) >> 1);
}
dest += halfSize;
src0 += childSize;
src1 += childSize;
src2 += childSize;
src3 += childSize;
}
}
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));
return false;
}
@ -514,6 +599,13 @@ HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (isLeaf) {
int size;
in >> size;
if (size == 0) {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
} else {
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
}
}
}
@ -522,11 +614,67 @@ void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf)
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
if (data) {
data->write(out, true);
} else {
out << 0;
}
}
}
bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer pointer = decodeInline<HeightfieldDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getContents().size());
}
}
if (maxSize == 0) {
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
return true;
}
int size = glm::sqrt(maxSize / (float)BYTES_PER_PIXEL);
QByteArray contents(size * size * BYTES_PER_PIXEL, 0);
int halfSize = size / 2;
for (int i = 0; i < MERGE_COUNT; i++) {
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
if (!child) {
continue;
}
const QByteArray& childContents = child->getContents();
int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL);
if (childSize != size) {
continue; // TODO: handle differently-sized children
}
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
if (yIndex == 0 && decodeInline<HeightfieldDataPointer>(children[i | (1 << Y_SHIFT)])) {
continue; // bottom is overriden by top
}
int Z_SHIFT = 2;
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL;
uchar* src0 = (uchar*)childContents.data();
uchar* src1 = src0 + BYTES_PER_PIXEL;
int childStride = childSize * BYTES_PER_PIXEL;
uchar* src2 = src0 + childStride;
uchar* src3 = src2 + BYTES_PER_PIXEL;
int halfStride = halfSize * BYTES_PER_PIXEL;
for (int z = 0; z < halfSize; z++) {
for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; ) {
*dest++ = ((int)(*src0++) + (int)(*src1++) + (int)(*src2++) + (int)(*src3++)) >> 2;
*dest++ = ((int)(*src0++) + (int)(*src1++) + (int)(*src2++) + (int)(*src3++)) >> 2;
*dest++ = ((int)(*src0++) + (int)(*src1++) + (int)(*src2++) + (int)(*src3++)) >> 2;
}
dest += halfStride;
src0 += childStride;
src1 += childStride;
src2 += childStride;
src3 += childStride;
}
}
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));
return false;
}

View file

@ -422,6 +422,7 @@ class HeightfieldData : public QSharedData {
public:
HeightfieldData(const QByteArray& contents);
HeightfieldData(Bitstream& in, int bytes, bool color);
const QByteArray& getContents() const { return _contents; }

View file

@ -639,6 +639,16 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference)
}
}
void Bitstream::writeAligned(const QByteArray& data) {
flush();
_underlying.device()->write(data);
}
QByteArray Bitstream::readAligned(int bytes) {
reset();
return _underlying.device()->read(bytes);
}
Bitstream& Bitstream::operator<<(bool value) {
if (value) {
_byte |= (1 << _position);

View file

@ -430,6 +430,11 @@ public:
template<class K, class V> void writeRawDelta(const QHash<K, V>& value, const QHash<K, V>& reference);
template<class K, class V> void readRawDelta(QHash<K, V>& value, const QHash<K, V>& reference);
/// Writes the specified array aligned on byte boundaries to avoid the inefficiency
/// of bit-twiddling (at the cost of up to seven bits of wasted space).
void writeAligned(const QByteArray& data);
QByteArray readAligned(int bytes);
Bitstream& operator<<(bool value);
Bitstream& operator>>(bool& value);