mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 20:44:14 +02:00
Working on heightfield transmission, merging.
This commit is contained in:
parent
ff01d4b80d
commit
82e442c115
6 changed files with 220 additions and 32 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue