From aeee3692868aabc99b39e68f635d957032d69794 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 28 Jul 2014 19:10:59 -0700 Subject: [PATCH 01/13] Working on heightfield implementation. --- .../shaders/metavoxel_heightfield.frag | 16 ++ .../shaders/metavoxel_heightfield.vert | 16 ++ interface/src/MetavoxelSystem.cpp | 250 ++++++++++++------ interface/src/MetavoxelSystem.h | 53 +++- .../metavoxels/src/AttributeRegistry.cpp | 28 +- libraries/metavoxels/src/AttributeRegistry.h | 45 ++++ libraries/metavoxels/src/MetavoxelData.cpp | 15 +- libraries/metavoxels/src/MetavoxelData.h | 7 +- 8 files changed, 337 insertions(+), 93 deletions(-) create mode 100644 interface/resources/shaders/metavoxel_heightfield.frag create mode 100644 interface/resources/shaders/metavoxel_heightfield.vert diff --git a/interface/resources/shaders/metavoxel_heightfield.frag b/interface/resources/shaders/metavoxel_heightfield.frag new file mode 100644 index 0000000000..de520d57ac --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield.frag @@ -0,0 +1,16 @@ +#version 120 + +// +// metavoxel_heightfield.frag +// fragment shader +// +// Created by Andrzej Kapolka on 7/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/interface/resources/shaders/metavoxel_heightfield.vert b/interface/resources/shaders/metavoxel_heightfield.vert new file mode 100644 index 0000000000..6b90bdc44d --- /dev/null +++ b/interface/resources/shaders/metavoxel_heightfield.vert @@ -0,0 +1,16 @@ +#version 120 + +// +// metavoxel_heighfield.vert +// vertex shader +// +// Created by Andrzej Kapolka on 7/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +void main(void) { + gl_Position = ftransform(); +} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 00d69c8c25..318bf65614 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -25,7 +25,7 @@ #include "MetavoxelSystem.h" #include "renderer/Model.h" -REGISTER_META_OBJECT(PointMetavoxelRendererImplementation) +REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(StaticModelRenderer) @@ -33,8 +33,10 @@ static int bufferPointVectorMetaTypeId = qRegisterMetaType(); void MetavoxelSystem::init() { MetavoxelClientManager::init(); - PointMetavoxelRendererImplementation::init(); - _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute()); + DefaultMetavoxelRendererImplementation::init(); + _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); + _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( + new BufferDataAttribute("heightfieldBuffer")); } MetavoxelLOD MetavoxelSystem::getLOD() { @@ -42,28 +44,31 @@ MetavoxelLOD MetavoxelSystem::getLOD() { return _lod; } -class SpannerSimulateVisitor : public SpannerVisitor { +class SimulateVisitor : public MetavoxelVisitor { public: - SpannerSimulateVisitor(float deltaTime); + SimulateVisitor(float deltaTime, const MetavoxelLOD& lod); - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + virtual int visit(MetavoxelInfo& info); private: float _deltaTime; }; -SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), QVector(), - Application::getInstance()->getMetavoxels()->getLOD()), +SimulateVisitor::SimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getRendererAttribute(), + QVector(), lod), _deltaTime(deltaTime) { } -bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - spanner->getRenderer()->simulate(_deltaTime); - return true; +int SimulateVisitor::visit(MetavoxelInfo& info) { + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + static_cast(info.inputValues.at(0).getInlineValue< + SharedObjectPointer>().data())->getImplementation()->simulate(*_data, _deltaTime, info, _lod); + return STOP_RECURSION; } void MetavoxelSystem::simulate(float deltaTime) { @@ -76,28 +81,8 @@ void MetavoxelSystem::simulate(float deltaTime) { BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); } - SpannerSimulateVisitor spannerSimulateVisitor(deltaTime); - guide(spannerSimulateVisitor); -} - -class SpannerRenderVisitor : public SpannerVisitor { -public: - - SpannerRenderVisitor(); - - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); -}; - -SpannerRenderVisitor::SpannerRenderVisitor() : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), QVector(), - Application::getInstance()->getMetavoxels()->getLOD(), - encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) { -} - -bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); - return true; + SimulateVisitor simulateVisitor(deltaTime, getLOD()); + guideToAugmented(simulateVisitor); } class RenderVisitor : public MetavoxelVisitor { @@ -125,9 +110,6 @@ int RenderVisitor::visit(MetavoxelInfo& info) { void MetavoxelSystem::render() { RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor); - - SpannerRenderVisitor spannerRenderVisitor; - guide(spannerRenderVisitor); } MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { @@ -239,6 +221,9 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); } +BufferData::~BufferData() { +} + PointBuffer::PointBuffer(const BufferPointVector& points) : _points(points) { } @@ -269,34 +254,76 @@ void PointBuffer::render() { _buffer.release(); } -PointBufferAttribute::PointBufferAttribute() : - InlineAttribute("pointBuffer") { +HeightfieldBuffer::HeightfieldBuffer(const QByteArray& height, const QByteArray& color, const QByteArray& normal) : + _height(height), + _color(color), + _normal(normal), + _heightTexture(QOpenGLTexture::Target2D), + _colorTexture(QOpenGLTexture::Target2D), + _normalTexture(QOpenGLTexture::Target2D) { } -bool PointBufferAttribute::merge(void*& parent, void* children[], bool postRead) const { - PointBufferPointer firstChild = decodeInline(children[0]); +void HeightfieldBuffer::render() { + // initialize textures, etc. on first render + if (!_heightTexture.isCreated()) { + int heightSize = glm::sqrt(_height.size()); + _heightTexture.setSize(heightSize, heightSize); + _heightTexture.setFormat(QOpenGLTexture::LuminanceFormat); + _heightTexture.allocateStorage(); + _heightTexture.setData(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, _height.data()); + _height.clear(); + + int colorSize = glm::sqrt(_color.size() / 3); + _colorTexture.setSize(colorSize, colorSize); + _colorTexture.setFormat(QOpenGLTexture::RGBFormat); + _colorTexture.allocateStorage(); + _colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _color.data()); + _color.clear(); + + int normalSize = glm::sqrt(_normal.size() / 3); + _normalTexture.setSize(normalSize, normalSize); + _normalTexture.setFormat(QOpenGLTexture::RGBFormat); + _normalTexture.allocateStorage(); + _normalTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _normal.data()); + _normal.clear(); + } + +} + +BufferDataAttribute::BufferDataAttribute(const QString& name) : + InlineAttribute(name) { +} + +bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const { + BufferDataPointer firstChild = decodeInline(children[0]); for (int i = 1; i < MERGE_COUNT; i++) { - if (firstChild != decodeInline(children[i])) { - *(PointBufferPointer*)&parent = _defaultValue; + if (firstChild != decodeInline(children[i])) { + *(BufferDataPointer*)&parent = _defaultValue; return false; } } - *(PointBufferPointer*)&parent = firstChild; + *(BufferDataPointer*)&parent = firstChild; return true; } -void PointMetavoxelRendererImplementation::init() { - if (!_program.isLinked()) { - _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); - _program.link(); +void DefaultMetavoxelRendererImplementation::init() { + if (!_pointProgram.isLinked()) { + _pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); + _pointProgram.link(); - _program.bind(); - _pointScaleLocation = _program.uniformLocation("pointScale"); - _program.release(); + _pointProgram.bind(); + _pointScaleLocation = _pointProgram.uniformLocation("pointScale"); + _pointProgram.release(); + + _heightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield.vert"); + _heightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield.frag"); + _heightfieldProgram.link(); } } -PointMetavoxelRendererImplementation::PointMetavoxelRendererImplementation() { +DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } class PointAugmentVisitor : public MetavoxelVisitor { @@ -344,7 +371,7 @@ int PointAugmentVisitor::visit(MetavoxelInfo& info) { if (info.size >= _pointLeafSize) { BufferPointVector swapPoints; _points.swap(swapPoints); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer( + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer( new PointBuffer(swapPoints)))); } return STOP_RECURSION; @@ -356,12 +383,30 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { } BufferPointVector swapPoints; _points.swap(swapPoints); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer( + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer( new PointBuffer(swapPoints)))); return true; } -void PointMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, +class HeightfieldAugmentVisitor : public MetavoxelVisitor { +public: + + HeightfieldAugmentVisitor(const MetavoxelLOD& lod); + + virtual int visit(MetavoxelInfo& info); +}; + +HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << + AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector() << + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) { +} + +int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { + return STOP_RECURSION; +} + +void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod) { // copy the previous buffers MetavoxelData expandedPrevious = previous; @@ -377,12 +422,62 @@ void PointMetavoxelRendererImplementation::augment(MetavoxelData& data, const Me PointAugmentVisitor visitor(lod); data.guideToDifferent(expandedPrevious, visitor); + + } -class PointRenderVisitor : public MetavoxelVisitor { +class SpannerSimulateVisitor : public SpannerVisitor { public: - PointRenderVisitor(const MetavoxelLOD& lod); + SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); + + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + +private: + + float _deltaTime; +}; + +SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), QVector(), lod), + _deltaTime(deltaTime) { +} + +bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { + spanner->getRenderer()->simulate(_deltaTime); + return true; +} + +void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime, + MetavoxelInfo& info, const MetavoxelLOD& lod) { + SpannerSimulateVisitor spannerSimulateVisitor(deltaTime, lod); + data.guide(spannerSimulateVisitor); +} + +class SpannerRenderVisitor : public SpannerVisitor { +public: + + SpannerRenderVisitor(const MetavoxelLOD& lod); + + virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); +}; + +SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), QVector(), + lod, encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) { +} + +bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { + spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); + return true; +} + +class BufferRenderVisitor : public MetavoxelVisitor { +public: + + BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); @@ -391,21 +486,23 @@ private: int _order; }; -PointRenderVisitor::PointRenderVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), - QVector(), lod), +BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) : + MetavoxelVisitor(QVector() << attribute, QVector(), lod), _order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) { } -int PointRenderVisitor::visit(MetavoxelInfo& info) { - PointBufferPointer buffer = info.inputValues.at(0).getInlineValue(); +int BufferRenderVisitor::visit(MetavoxelInfo& info) { + BufferDataPointer buffer = info.inputValues.at(0).getInlineValue(); if (buffer) { buffer->render(); } return info.isLeaf ? STOP_RECURSION : _order; } -void PointMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { +void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { + SpannerRenderVisitor spannerRenderVisitor(lod); + data.guide(spannerRenderVisitor); + int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const int VIEWPORT_WIDTH_INDEX = 2; @@ -416,8 +513,8 @@ void PointMetavoxelRendererImplementation::render(MetavoxelData& data, Metavoxel float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(), Application::getInstance()->getViewFrustum()->getNearTopRight()); - _program.bind(); - _program.setUniformValue(_pointScaleLocation, viewportDiagonal * + _pointProgram.bind(); + _pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal * Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal); glEnableClientState(GL_VERTEX_ARRAY); @@ -428,10 +525,8 @@ void PointMetavoxelRendererImplementation::render(MetavoxelData& data, Metavoxel glDisable(GL_BLEND); - PointRenderVisitor visitor(lod); - data.guide(visitor); - - glEnable(GL_BLEND); + BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod); + data.guide(pointRenderVisitor); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); @@ -439,11 +534,18 @@ void PointMetavoxelRendererImplementation::render(MetavoxelData& data, Metavoxel glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); - _program.release(); + _pointProgram.release(); + + BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), + lod); + data.guide(heightfieldRenderVisitor); + + glEnable(GL_BLEND); } - -ProgramObject PointMetavoxelRendererImplementation::_program; -int PointMetavoxelRendererImplementation::_pointScaleLocation; + +ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; +int DefaultMetavoxelRendererImplementation::_pointScaleLocation; +ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram; static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { GLdouble coefficients[] = { x, y, z, w }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 4a5b99aa47..be1a02643f 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ public: virtual MetavoxelLOD getLOD(); const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } + const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } void simulate(float deltaTime); void render(); @@ -49,6 +51,7 @@ private: void guideToAugmented(MetavoxelVisitor& visitor); AttributePointer _pointBufferAttribute; + AttributePointer _heightfieldBufferAttribute; MetavoxelLOD _lod; QReadWriteLock _lodLock; @@ -92,13 +95,24 @@ private: QReadWriteLock _augmentedDataLock; }; +/// Base class for cached static buffers. +class BufferData : public QSharedData { +public: + + virtual ~BufferData(); + + virtual void render() = 0; +}; + +typedef QExplicitlySharedDataPointer BufferDataPointer; + /// Contains the information necessary to render a group of points. -class PointBuffer : public QSharedData { +class PointBuffer : public BufferData { public: PointBuffer(const BufferPointVector& points); - void render(); + virtual void render(); private: @@ -107,36 +121,55 @@ private: int _pointCount; }; -typedef QExplicitlySharedDataPointer PointBufferPointer; +/// Contains the information necessary to render a heightfield block. +class HeightfieldBuffer : public BufferData { +public: + + HeightfieldBuffer(const QByteArray& height, const QByteArray& color, const QByteArray& normal); + + virtual void render(); -/// A client-side attribute that stores point buffers. -class PointBufferAttribute : public InlineAttribute { +private: + + QByteArray _height; + QByteArray _color; + QByteArray _normal; + QOpenGLTexture _heightTexture; + QOpenGLTexture _colorTexture; + QOpenGLTexture _normalTexture; +}; + +/// A client-side attribute that stores renderable buffers. +class BufferDataAttribute : public InlineAttribute { Q_OBJECT public: - Q_INVOKABLE PointBufferAttribute(); + Q_INVOKABLE BufferDataAttribute(const QString& name = QString()); virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; /// Renders metavoxels as points. -class PointMetavoxelRendererImplementation : public MetavoxelRendererImplementation { +class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation { Q_OBJECT public: static void init(); - Q_INVOKABLE PointMetavoxelRendererImplementation(); + Q_INVOKABLE DefaultMetavoxelRendererImplementation(); virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); + virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod); virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod); private: - static ProgramObject _program; - static int _pointScaleLocation; + static ProgramObject _pointProgram; + static int _pointScaleLocation; + + static ProgramObject _heightfieldProgram; }; /// Base class for spanner renderers; provides clipping. diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 7c7ded0fb7..4f4da7adf2 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -21,6 +21,8 @@ REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(SpannerQRgbAttribute) REGISTER_META_OBJECT(SpannerPackedNormalAttribute) +REGISTER_META_OBJECT(HeightfieldAttribute) +REGISTER_META_OBJECT(HeightfieldColorAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -37,13 +39,15 @@ AttributeRegistry::AttributeRegistry() : _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, new DefaultMetavoxelGuide()))), _rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject, - new PointMetavoxelRenderer()))), + new DefaultMetavoxelRenderer()))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), _spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))), _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))), - _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))) { + _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))), + _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), + _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))) { // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -451,6 +455,26 @@ AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& paren return AttributeValue(parentValue.getAttribute()); } +HeightfieldData::HeightfieldData(const QByteArray& contents) : + _contents(contents) { +} + +HeightfieldAttribute::HeightfieldAttribute(const QString& name) : + InlineAttribute(name) { +} + +bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const { + return false; +} + +HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : + InlineAttribute(name) { +} + +bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const { + return false; +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index d07503335f..262ef89462 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -94,6 +94,12 @@ public: /// Returns a reference to the standard "spannerMask" attribute. const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } + /// Returns a reference to the standard HeightfieldPointer "heightfield" attribute. + const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; } + + /// Returns a reference to the standard HeightfieldColorPointer "heightfieldColor" attribute. + const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -109,6 +115,8 @@ private: AttributePointer _spannerColorAttribute; AttributePointer _spannerNormalAttribute; AttributePointer _spannerMaskAttribute; + AttributePointer _heightfieldAttribute; + AttributePointer _heightfieldColorAttribute; }; /// Converts a value to a void pointer. @@ -408,6 +416,43 @@ public: virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; +/// Contains a block of heightfield data. +class HeightfieldData : public QSharedData { +public: + + HeightfieldData(const QByteArray& contents); + + const QByteArray& getContents() const { return _contents; } + +private: + + QByteArray _contents; +}; + +typedef QExplicitlySharedDataPointer HeightfieldDataPointer; + +/// An attribute that stores heightfield data. +class HeightfieldAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE HeightfieldAttribute(const QString& name = QString()); + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + +/// An attribute that stores heightfield colors. +class HeightfieldColorAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE HeightfieldColorAttribute(const QString& name = QString()); + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 3bf43ef8d4..29f18f5cc1 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -26,7 +26,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(ScriptedMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) REGISTER_META_OBJECT(MetavoxelRenderer) -REGISTER_META_OBJECT(PointMetavoxelRenderer) +REGISTER_META_OBJECT(DefaultMetavoxelRenderer) REGISTER_META_OBJECT(Spanner) REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(StaticModel) @@ -1879,20 +1879,27 @@ void MetavoxelRendererImplementation::init(MetavoxelRenderer* renderer) { void MetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod) { + // nothing by default +} + +void MetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime, + MetavoxelInfo& info, const MetavoxelLOD& lod) { + // nothing by default } void MetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { + // nothing by default } QByteArray MetavoxelRenderer::getImplementationClassName() const { return "MetavoxelRendererImplementation"; } -PointMetavoxelRenderer::PointMetavoxelRenderer() { +DefaultMetavoxelRenderer::DefaultMetavoxelRenderer() { } -QByteArray PointMetavoxelRenderer::getImplementationClassName() const { - return "PointMetavoxelRendererImplementation"; +QByteArray DefaultMetavoxelRenderer::getImplementationClassName() const { + return "DefaultMetavoxelRendererImplementation"; } const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 2dc778cf71..46376601c1 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -555,6 +555,7 @@ public: virtual void init(MetavoxelRenderer* renderer); virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); + virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod); virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod); protected: @@ -562,13 +563,13 @@ protected: MetavoxelRenderer* _renderer; }; -/// Renders metavoxels as points. -class PointMetavoxelRenderer : public MetavoxelRenderer { +/// The standard, usual renderer. +class DefaultMetavoxelRenderer : public MetavoxelRenderer { Q_OBJECT public: - Q_INVOKABLE PointMetavoxelRenderer(); + Q_INVOKABLE DefaultMetavoxelRenderer(); virtual QByteArray getImplementationClassName() const; }; From d04eb6261e1d07d42f7bbcd5b82441873e9a16d1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 29 Jul 2014 14:27:51 -0700 Subject: [PATCH 02/13] More work on heightfields. --- interface/src/MetavoxelSystem.cpp | 191 +++++++++++++++++++++++---- interface/src/MetavoxelSystem.h | 26 +++- interface/src/ui/MetavoxelEditor.cpp | 93 ++++++++++++- interface/src/ui/MetavoxelEditor.h | 31 +++++ 4 files changed, 308 insertions(+), 33 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 318bf65614..f43ec1e001 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -254,15 +254,22 @@ void PointBuffer::render() { _buffer.release(); } -HeightfieldBuffer::HeightfieldBuffer(const QByteArray& height, const QByteArray& color, const QByteArray& normal) : +HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, + const QByteArray& height, const QByteArray& color) : + _translation(translation), + _scale(scale), _height(height), _color(color), - _normal(normal), _heightTexture(QOpenGLTexture::Target2D), - _colorTexture(QOpenGLTexture::Target2D), - _normalTexture(QOpenGLTexture::Target2D) { + _colorTexture(QOpenGLTexture::Target2D) { } +class HeightfieldPoint { +public: + glm::vec2 textureCoord; + glm::vec3 vertex; +}; + void HeightfieldBuffer::render() { // initialize textures, etc. on first render if (!_heightTexture.isCreated()) { @@ -273,21 +280,116 @@ void HeightfieldBuffer::render() { _heightTexture.setData(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, _height.data()); _height.clear(); - int colorSize = glm::sqrt(_color.size() / 3); - _colorTexture.setSize(colorSize, colorSize); + if (!_color.isEmpty()) { + int colorSize = glm::sqrt(_color.size() / 3); + _colorTexture.setSize(colorSize, colorSize); + } _colorTexture.setFormat(QOpenGLTexture::RGBFormat); _colorTexture.allocateStorage(); - _colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _color.data()); - _color.clear(); + if (!_color.isEmpty()) { + _colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _color.data()); + _color.clear(); + + } else { + const quint8 WHITE_COLOR[] = { 255, 255, 255 }; + _colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, const_cast(WHITE_COLOR)); + } + } + // create the buffer objects lazily + int size = _heightTexture.width(); + int sizeWithSkirt = size + 2; + int vertexCount = sizeWithSkirt * sizeWithSkirt; + int rows = sizeWithSkirt - 1; + int indexCount = rows * rows * 4; + BufferPair& bufferPair = _bufferPairs[size]; + if (!bufferPair.first.isCreated()) { + QVector vertices(vertexCount); + HeightfieldPoint* point = vertices.data(); - int normalSize = glm::sqrt(_normal.size() / 3); - _normalTexture.setSize(normalSize, normalSize); - _normalTexture.setFormat(QOpenGLTexture::RGBFormat); - _normalTexture.allocateStorage(); - _normalTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _normal.data()); - _normal.clear(); + float step = 1.0f / (size - 1); + float z = -step; + for (int i = 0; i < sizeWithSkirt; i++, z += step) { + float x = -step; + const float SKIRT_LENGTH = 1.0f; + float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f; + for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) { + point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z); + point->textureCoord = glm::vec2(x, z); + } + } + + bufferPair.first.setUsagePattern(QOpenGLBuffer::StaticDraw); + bufferPair.first.create(); + bufferPair.first.bind(); + bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint)); + + QVector indices(indexCount); + int* index = indices.data(); + for (int i = 0; i < rows; i++) { + int lineIndex = i * sizeWithSkirt; + int nextLineIndex = (i + 1) * sizeWithSkirt; + for (int j = 0; j < rows; j++) { + *index++ = lineIndex + j; + *index++ = lineIndex + j + 1; + *index++ = nextLineIndex + j; + *index++ = nextLineIndex + j + 1; + } + } + + bufferPair.second = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); + bufferPair.second.create(); + bufferPair.second.bind(); + bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int)); + + } else { + bufferPair.first.bind(); + bufferPair.second.bind(); } + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glPushMatrix(); + glTranslatef(_translation.x, _translation.y, _translation.z); + glScalef(_scale, _scale, _scale); + + _heightTexture.bind(0); + _colorTexture.bind(1); + + glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + _colorTexture.release(1); + _heightTexture.release(0); + + glPopMatrix(); + + bufferPair.first.release(); + bufferPair.second.release(); +} + +QHash HeightfieldBuffer::_bufferPairs; + +void HeightfieldPreview::render(const glm::vec3& translation, float scale) const { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind(); + + glPushMatrix(); + glTranslatef(translation.x, translation.y, translation.z); + glScalef(scale, scale, scale); + + foreach (const BufferDataPointer& buffer, _buffers) { + buffer->render(); + } + + glPopMatrix(); + + DefaultMetavoxelRendererImplementation::getHeightfieldProgram().release(); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); } BufferDataAttribute::BufferDataAttribute(const QString& name) : @@ -368,11 +470,14 @@ int PointAugmentVisitor::visit(MetavoxelInfo& info) { { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; _points.append(point); } - if (info.size >= _pointLeafSize) { - BufferPointVector swapPoints; - _points.swap(swapPoints); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer( - new PointBuffer(swapPoints)))); + if (info.size >= _pointLeafSize) { + PointBuffer* buffer = NULL; + if (!_points.isEmpty()) { + BufferPointVector swapPoints; + _points.swap(swapPoints); + buffer = new PointBuffer(swapPoints); + } + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); } return STOP_RECURSION; } @@ -381,10 +486,13 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { if (info.size != _pointLeafSize) { return false; } - BufferPointVector swapPoints; - _points.swap(swapPoints); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer( - new PointBuffer(swapPoints)))); + PointBuffer* buffer = NULL; + if (!_points.isEmpty()) { + BufferPointVector swapPoints; + _points.swap(swapPoints); + buffer = new PointBuffer(swapPoints); + } + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); return true; } @@ -403,7 +511,18 @@ HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) : } int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) { - return STOP_RECURSION; + if (info.isLeaf) { + HeightfieldBuffer* buffer = NULL; + HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue(); + if (height) { + HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue(); + buffer = new HeightfieldBuffer(info.minimum, info.size, height->getContents(), + color ? color->getContents() : QByteArray()); + } + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); + return STOP_RECURSION; + } + return DEFAULT_ORDER; } void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, @@ -419,11 +538,19 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const data.setRoot(pointBufferAttribute, root); root->incrementReferenceCount(); } + const AttributePointer& heightfieldBufferAttribute = + Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(); + root = expandedPrevious.getRoot(heightfieldBufferAttribute); + if (root) { + data.setRoot(heightfieldBufferAttribute, root); + root->incrementReferenceCount(); + } - PointAugmentVisitor visitor(lod); - data.guideToDifferent(expandedPrevious, visitor); - + PointAugmentVisitor pointAugmentVisitor(lod); + data.guideToDifferent(expandedPrevious, pointAugmentVisitor); + HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod); + data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor); } class SpannerSimulateVisitor : public SpannerVisitor { @@ -530,16 +657,24 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); _pointProgram.release(); + _heightfieldProgram.bind(); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod); data.guide(heightfieldRenderVisitor); + _heightfieldProgram.release(); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glEnable(GL_BLEND); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index be1a02643f..1ee037f8c0 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -125,18 +125,34 @@ private: class HeightfieldBuffer : public BufferData { public: - HeightfieldBuffer(const QByteArray& height, const QByteArray& color, const QByteArray& normal); + HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color); virtual void render(); private: + glm::vec3 _translation; + float _scale; QByteArray _height; QByteArray _color; - QByteArray _normal; QOpenGLTexture _heightTexture; QOpenGLTexture _colorTexture; - QOpenGLTexture _normalTexture; + + typedef QPair BufferPair; + static QHash _bufferPairs; +}; + +/// Convenience class for rendering a preview of a heightfield. +class HeightfieldPreview { +public: + + void setBuffers(const QVector& buffers) { _buffers = buffers; } + + void render(const glm::vec3& translation, float scale) const; + +private: + + QVector _buffers; }; /// A client-side attribute that stores renderable buffers. @@ -157,7 +173,9 @@ class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplement public: static void init(); - + + static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; } + Q_INVOKABLE DefaultMetavoxelRendererImplementation(); virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 1f0c2498c5..80088c040a 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -15,11 +15,12 @@ #include #include #include +#include #include #include #include #include -#include +#include #include #include #include @@ -113,6 +114,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); addTool(new SetSpannerTool(this)); + addTool(new ImportHeightfieldTool(this)); updateAttributes(); @@ -891,3 +893,92 @@ void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedOb QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds, spannerData->getVoxelizationGranularity(), directionImages)); } + +ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : + MetavoxelTool(editor, "Import Heightfield", false) { + + QWidget* widget = new QWidget(); + QFormLayout* form = new QFormLayout(); + widget->setLayout(form); + layout()->addWidget(widget); + + form->addRow("Height:", _height = new QPushButton()); + connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); + form->addRow("Color:", _color = new QPushButton()); + connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); +} + +bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("HeightfieldAttribute"); +} + +void ImportHeightfieldTool::render() { + _preview.render(glm::vec3(), 1.0f); +} + +void ImportHeightfieldTool::selectHeightFile() { + QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(), "Images (*.png *.jpg)"); + if (filename.isNull()) { + return; + } + if (!_heightImage.load(filename)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + _heightImage = _heightImage.convertToFormat(QImage::Format_RGB888); + _height->setText(filename); + updatePreview(); +} + +void ImportHeightfieldTool::selectColorFile() { + QString filename = QFileDialog::getOpenFileName(this, "Select Color Image", QString(), "Images (*.png *.jpg)"); + if (filename.isNull()) { + return; + } + if (!_colorImage.load(filename)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + _colorImage = _colorImage.convertToFormat(QImage::Format_RGB888); + _color->setText(filename); + updatePreview(); +} + +const int BLOCK_SIZE = 64; + +void ImportHeightfieldTool::updatePreview() { + QVector buffers; + if (_heightImage.width() > 0 && _heightImage.height() > 0) { + float z = 0.0f; + for (int i = 0; i < _heightImage.height(); i += BLOCK_SIZE, z++) { + float x = 0.0f; + for (int j = 0; j < _heightImage.width(); j += BLOCK_SIZE, x++) { + QByteArray height(BLOCK_SIZE * BLOCK_SIZE, 0); + int rows = qMin(BLOCK_SIZE, _heightImage.height() - i); + int columns = qMin(BLOCK_SIZE, _heightImage.width() - j); + const int BYTES_PER_COLOR = 3; + for (int y = 0; y < rows; y++) { + uchar* src = _heightImage.scanLine(i + y); + char* dest = height.data() + y * BLOCK_SIZE; + for (int x = 0; x < columns; x++) { + *dest++ = *src; + src += BYTES_PER_COLOR; + } + } + + QByteArray color; + if (!_colorImage.isNull()) { + color = QByteArray(BLOCK_SIZE * BLOCK_SIZE * BYTES_PER_COLOR, 0); + rows = qMax(0, qMin(BLOCK_SIZE, _colorImage.height() - i)); + columns = qMax(0, qMin(BLOCK_SIZE, _colorImage.width() - j)); + for (int y = 0; y < rows; y++) { + memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR, _colorImage.scanLine(i + y), + columns * BYTES_PER_COLOR); + } + } + buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); + } + } + } + _preview.setBuffers(buffers); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 79ffd1e64c..4c97da50bb 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -15,6 +15,7 @@ #include #include +#include "MetavoxelSystem.h" #include "renderer/ProgramObject.h" class QComboBox; @@ -223,4 +224,34 @@ protected: virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); }; +/// Allows importing a heightfield. +class ImportHeightfieldTool : public MetavoxelTool { + Q_OBJECT + +public: + + ImportHeightfieldTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + + virtual void render(); + +private slots: + + void selectHeightFile(); + void selectColorFile(); + +private: + + void updatePreview(); + + QPushButton* _height; + QPushButton* _color; + + QImage _heightImage; + QImage _colorImage; + + HeightfieldPreview _preview; +}; + #endif // hifi_MetavoxelEditor_h From 68244797622263f48aad566d034cb5ce4550e7b5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 29 Jul 2014 16:06:35 -0700 Subject: [PATCH 03/13] Progress on rendering heightfield buffers. --- .../shaders/metavoxel_heightfield.frag | 11 ++++++++- .../shaders/metavoxel_heightfield.vert | 19 ++++++++++++++- interface/src/MetavoxelSystem.cpp | 23 +++++++++++++++---- interface/src/ui/MetavoxelEditor.cpp | 5 ++-- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/interface/resources/shaders/metavoxel_heightfield.frag b/interface/resources/shaders/metavoxel_heightfield.frag index de520d57ac..f99a0a6403 100644 --- a/interface/resources/shaders/metavoxel_heightfield.frag +++ b/interface/resources/shaders/metavoxel_heightfield.frag @@ -11,6 +11,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the diffuse texture +uniform sampler2D diffuseMap; + +// the interpolated normal +varying vec4 normal; + void main(void) { - gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + // compute the base color based on OpenGL lighting model + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position))); + gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st); } diff --git a/interface/resources/shaders/metavoxel_heightfield.vert b/interface/resources/shaders/metavoxel_heightfield.vert index 6b90bdc44d..a5e7ec66c1 100644 --- a/interface/resources/shaders/metavoxel_heightfield.vert +++ b/interface/resources/shaders/metavoxel_heightfield.vert @@ -11,6 +11,23 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the height texture +uniform sampler2D heightMap; + +// the interpolated normal +varying vec4 normal; + void main(void) { - gl_Position = ftransform(); + // transform and store the normal for interpolation + normal = normalize(gl_ModelViewMatrix * vec4(0.0, 1.0, 0.0, 0.0)); + + // pass along the vertex color + gl_FrontColor = gl_Color; + + // pass along the texture coordinates + gl_TexCoord[0] = gl_MultiTexCoord0; + + // add the height to the position + gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + + vec4(0.0, texture2D(heightMap, gl_MultiTexCoord0.st).r, 0.0, 0.0)); } diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index f43ec1e001..c090edcbef 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -275,6 +275,9 @@ void HeightfieldBuffer::render() { 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()); @@ -284,15 +287,18 @@ void HeightfieldBuffer::render() { int colorSize = glm::sqrt(_color.size() / 3); _colorTexture.setSize(colorSize, colorSize); } + _colorTexture.setAutoMipMapGenerationEnabled(false); + _colorTexture.setMinificationFilter(QOpenGLTexture::Linear); + _colorTexture.setWrapMode(QOpenGLTexture::ClampToEdge); _colorTexture.setFormat(QOpenGLTexture::RGBFormat); _colorTexture.allocateStorage(); if (!_color.isEmpty()) { - _colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _color.data()); + _colorTexture.setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, _color.data()); _color.clear(); } else { const quint8 WHITE_COLOR[] = { 255, 255, 255 }; - _colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, const_cast(WHITE_COLOR)); + _colorTexture.setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, const_cast(WHITE_COLOR)); } } // create the buffer objects lazily @@ -310,7 +316,7 @@ void HeightfieldBuffer::render() { float z = -step; for (int i = 0; i < sizeWithSkirt; i++, z += step) { float x = -step; - const float SKIRT_LENGTH = 1.0f; + const float SKIRT_LENGTH = 0.25f; float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f; for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) { point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z); @@ -331,8 +337,8 @@ void HeightfieldBuffer::render() { for (int j = 0; j < rows; j++) { *index++ = lineIndex + j; *index++ = lineIndex + j + 1; - *index++ = nextLineIndex + j; *index++ = nextLineIndex + j + 1; + *index++ = nextLineIndex + j; } } @@ -371,6 +377,8 @@ void HeightfieldBuffer::render() { QHash HeightfieldBuffer::_bufferPairs; void HeightfieldPreview::render(const glm::vec3& translation, float scale) const { + glColor4f(1.0f, 1.0f, 1.0f, 0.75f); + glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -422,6 +430,11 @@ void DefaultMetavoxelRendererImplementation::init() { _heightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/metavoxel_heightfield.frag"); _heightfieldProgram.link(); + + _heightfieldProgram.bind(); + _heightfieldProgram.setUniformValue("heightMap", 0); + _heightfieldProgram.setUniformValue("diffuseMap", 1); + _heightfieldProgram.release(); } } @@ -662,6 +675,8 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox _pointProgram.release(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + _heightfieldProgram.bind(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 80088c040a..36fbf5d9e7 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -945,14 +945,15 @@ void ImportHeightfieldTool::selectColorFile() { } const int BLOCK_SIZE = 64; +const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1; void ImportHeightfieldTool::updatePreview() { QVector buffers; if (_heightImage.width() > 0 && _heightImage.height() > 0) { float z = 0.0f; - for (int i = 0; i < _heightImage.height(); i += BLOCK_SIZE, z++) { + for (int i = 0; i < _heightImage.height(); i += BLOCK_ADVANCEMENT, z++) { float x = 0.0f; - for (int j = 0; j < _heightImage.width(); j += BLOCK_SIZE, x++) { + for (int j = 0; j < _heightImage.width(); j += BLOCK_ADVANCEMENT, x++) { QByteArray height(BLOCK_SIZE * BLOCK_SIZE, 0); int rows = qMin(BLOCK_SIZE, _heightImage.height() - i); int columns = qMin(BLOCK_SIZE, _heightImage.width() - j); From 436727dfdb5623b0d82e6f4875e1b6081dee8682 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 29 Jul 2014 16:33:36 -0700 Subject: [PATCH 04/13] Added scale field, fixed height/color extraction. --- interface/src/ui/MetavoxelEditor.cpp | 15 ++++++++++----- interface/src/ui/MetavoxelEditor.h | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 36fbf5d9e7..e1f7b38466 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -906,6 +906,11 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); form->addRow("Color:", _color = new QPushButton()); connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); + form->addRow("Scale:", _scale = new QDoubleSpinBox()); + _scale->setMinimum(-FLT_MAX); + _scale->setMaximum(FLT_MAX); + _scale->setPrefix("2^"); + _scale->setValue(1.0); } bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const { @@ -913,7 +918,7 @@ bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const { } void ImportHeightfieldTool::render() { - _preview.render(glm::vec3(), 1.0f); + _preview.render(glm::vec3(), pow(2.0, _scale->value())); } void ImportHeightfieldTool::selectHeightFile() { @@ -944,7 +949,7 @@ void ImportHeightfieldTool::selectColorFile() { updatePreview(); } -const int BLOCK_SIZE = 64; +const int BLOCK_SIZE = 32; const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1; void ImportHeightfieldTool::updatePreview() { @@ -959,7 +964,7 @@ void ImportHeightfieldTool::updatePreview() { int columns = qMin(BLOCK_SIZE, _heightImage.width() - j); const int BYTES_PER_COLOR = 3; for (int y = 0; y < rows; y++) { - uchar* src = _heightImage.scanLine(i + y); + uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR; char* dest = height.data() + y * BLOCK_SIZE; for (int x = 0; x < columns; x++) { *dest++ = *src; @@ -973,8 +978,8 @@ void ImportHeightfieldTool::updatePreview() { rows = qMax(0, qMin(BLOCK_SIZE, _colorImage.height() - i)); columns = qMax(0, qMin(BLOCK_SIZE, _colorImage.width() - j)); for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR, _colorImage.scanLine(i + y), - columns * BYTES_PER_COLOR); + memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR, + _colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR); } } buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 4c97da50bb..1fbae86e1a 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -247,6 +247,7 @@ private: QPushButton* _height; QPushButton* _color; + QDoubleSpinBox* _scale; QImage _heightImage; QImage _colorImage; From 496f4a184eb7f9d7e286b2e76619f8794b23f5d6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 29 Jul 2014 17:36:52 -0700 Subject: [PATCH 05/13] Translation, etc. --- interface/src/ui/MetavoxelEditor.cpp | 16 +++++++++++----- interface/src/ui/MetavoxelEditor.h | 4 +++- libraries/metavoxels/src/MetavoxelUtil.cpp | 10 +++++++--- libraries/metavoxels/src/MetavoxelUtil.h | 4 ++++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index e1f7b38466..70573b02c7 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "Application.h" #include "MetavoxelEditor.h" @@ -902,15 +903,16 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : widget->setLayout(form); layout()->addWidget(widget); - form->addRow("Height:", _height = new QPushButton()); - connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); - form->addRow("Color:", _color = new QPushButton()); - connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); + form->addRow("Translation:", _translation = new Vec3Editor(widget)); form->addRow("Scale:", _scale = new QDoubleSpinBox()); _scale->setMinimum(-FLT_MAX); _scale->setMaximum(FLT_MAX); _scale->setPrefix("2^"); _scale->setValue(1.0); + form->addRow("Height:", _height = new QPushButton()); + connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); + form->addRow("Color:", _color = new QPushButton()); + connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); } bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const { @@ -918,7 +920,11 @@ bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const { } void ImportHeightfieldTool::render() { - _preview.render(glm::vec3(), pow(2.0, _scale->value())); + float scale = pow(2.0, _scale->value()); + _translation->setSingleStep(scale); + glm::vec3 quantizedTranslation = scale * glm::floor(_translation->getValue() / scale); + _translation->setValue(quantizedTranslation); + _preview.render(quantizedTranslation, scale); } void ImportHeightfieldTool::selectHeightFile() { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 1fbae86e1a..bc9dfdf741 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -26,6 +26,7 @@ class QPushButton; class QScrollArea; class MetavoxelTool; +class Vec3Editor; /// Allows editing metavoxels. class MetavoxelEditor : public QWidget { @@ -245,9 +246,10 @@ private: void updatePreview(); + Vec3Editor* _translation; + QDoubleSpinBox* _scale; QPushButton* _height; QPushButton* _color; - QDoubleSpinBox* _scale; QImage _heightImage; QImage _colorImage; diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index d35b9a698a..a96b831dc1 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -401,6 +401,12 @@ BaseVec3Editor::BaseVec3Editor(QWidget* parent) : QWidget(parent) { layout->addWidget(_z = createComponentBox()); } +void BaseVec3Editor::setSingleStep(double singleStep) { + _x->setSingleStep(singleStep); + _y->setSingleStep(singleStep); + _z->setSingleStep(singleStep); +} + QDoubleSpinBox* BaseVec3Editor::createComponentBox() { QDoubleSpinBox* box = new QDoubleSpinBox(); box->setMinimum(-FLT_MAX); @@ -411,9 +417,7 @@ QDoubleSpinBox* BaseVec3Editor::createComponentBox() { } Vec3Editor::Vec3Editor(QWidget* parent) : BaseVec3Editor(parent) { - _x->setSingleStep(0.01); - _y->setSingleStep(0.01); - _z->setSingleStep(0.01); + setSingleStep(0.01); } static void setComponentValue(QDoubleSpinBox* box, double value) { diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 83aac1318e..021fbcb20d 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -153,6 +153,8 @@ public: BaseVec3Editor(QWidget* parent); + void setSingleStep(double singleStep); + protected slots: virtual void updateValue() = 0; @@ -175,6 +177,8 @@ public: Vec3Editor(QWidget* parent); + const glm::vec3& getValue() const { return _value; } + signals: void valueChanged(const glm::vec3& vector); From 1ea9b0ccbcf4141fd9ab6988d6a00fb569570fa0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 29 Jul 2014 19:13:43 -0700 Subject: [PATCH 06/13] Working on heightfield encoding. --- interface/src/MetavoxelSystem.cpp | 13 +++-- interface/src/MetavoxelSystem.h | 12 +++- interface/src/ui/MetavoxelEditor.cpp | 28 +++++++++- interface/src/ui/MetavoxelEditor.h | 1 + .../metavoxels/src/AttributeRegistry.cpp | 55 +++++++++++++++++++ libraries/metavoxels/src/AttributeRegistry.h | 11 ++++ 6 files changed, 112 insertions(+), 8 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index c090edcbef..e1479f8f02 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -255,11 +255,12 @@ void PointBuffer::render() { } HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, - const QByteArray& height, const QByteArray& color) : + const QByteArray& height, const QByteArray& color, bool clearAfterLoading) : _translation(translation), _scale(scale), _height(height), _color(color), + _clearAfterLoading(clearAfterLoading), _heightTexture(QOpenGLTexture::Target2D), _colorTexture(QOpenGLTexture::Target2D) { } @@ -281,8 +282,9 @@ void HeightfieldBuffer::render() { _heightTexture.setFormat(QOpenGLTexture::LuminanceFormat); _heightTexture.allocateStorage(); _heightTexture.setData(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, _height.data()); - _height.clear(); - + if (_clearAfterLoading) { + _height.clear(); + } if (!_color.isEmpty()) { int colorSize = glm::sqrt(_color.size() / 3); _colorTexture.setSize(colorSize, colorSize); @@ -294,8 +296,9 @@ void HeightfieldBuffer::render() { _colorTexture.allocateStorage(); if (!_color.isEmpty()) { _colorTexture.setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, _color.data()); - _color.clear(); - + if (_clearAfterLoading) { + _color.clear(); + } } else { const quint8 WHITE_COLOR[] = { 255, 255, 255 }; _colorTexture.setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, const_cast(WHITE_COLOR)); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 1ee037f8c0..89ea7dcbda 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -125,7 +125,15 @@ private: class HeightfieldBuffer : public BufferData { public: - HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color); + /// Creates a new heightfield buffer. + /// \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); + + const glm::vec3& getTranslation() const { return _translation; } + + const QByteArray& getHeight() const { return _height; } + const QByteArray& getColor() const { return _color; } virtual void render(); @@ -135,6 +143,7 @@ private: float _scale; QByteArray _height; QByteArray _color; + bool _clearAfterLoading; QOpenGLTexture _heightTexture; QOpenGLTexture _colorTexture; @@ -147,6 +156,7 @@ class HeightfieldPreview { public: void setBuffers(const QVector& buffers) { _buffers = buffers; } + const QVector& getBuffers() const { return _buffers; } void render(const glm::vec3& translation, float scale) const; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 70573b02c7..3f3198116f 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -908,11 +908,15 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : _scale->setMinimum(-FLT_MAX); _scale->setMaximum(FLT_MAX); _scale->setPrefix("2^"); - _scale->setValue(1.0); + _scale->setValue(3.0); form->addRow("Height:", _height = new QPushButton()); connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); form->addRow("Color:", _color = new QPushButton()); connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); + + QPushButton* applyButton = new QPushButton("Apply"); + layout()->addWidget(applyButton); + connect(applyButton, &QAbstractButton::clicked, this, &ImportHeightfieldTool::apply); } bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const { @@ -955,6 +959,26 @@ void ImportHeightfieldTool::selectColorFile() { updatePreview(); } +void ImportHeightfieldTool::apply() { + float scale = pow(2.0, _scale->value()); + foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) { + HeightfieldBuffer* buffer = static_cast(bufferData.data()); + MetavoxelData data; + data.setSize(scale); + HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight())); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( + AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); + if (!buffer->getColor().isEmpty()) { + HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor())); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( + AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); + } + MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( + _translation->getValue() + buffer->getTranslation() * scale, data)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); + } +} + const int BLOCK_SIZE = 32; const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1; @@ -988,7 +1012,7 @@ void ImportHeightfieldTool::updatePreview() { _colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR); } } - buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); + buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color, false))); } } } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index bc9dfdf741..2545ff57ba 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -241,6 +241,7 @@ private slots: void selectHeightFile(); void selectColorFile(); + void apply(); private: diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 4f4da7adf2..fefbbc3cd8 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include #include #include #include @@ -459,10 +461,49 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) : _contents(contents) { } +void HeightfieldData::write(Bitstream& out, bool color) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + QImage image; + const int BYTES_PER_PIXEL = 3; + if (color) { + int size = glm::sqrt(_contents.size() / (double)BYTES_PER_PIXEL); + image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); + } else { + int size = glm::sqrt((double)_contents.size()); + image = QImage(size, size, QImage::Format_RGB888); + uchar* dest = image.bits(); + for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) { + *dest++ = *src; + *dest++ = *src; + *dest++ = *src; + } + } + QBuffer buffer(&_encoded); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "JPG"); + } + +} + HeightfieldAttribute::HeightfieldAttribute(const QString& name) : InlineAttribute(name) { } +void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + } +} + +void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + HeightfieldDataPointer data = decodeInline(value); + if (data) { + data->write(out, false); + } + } +} + bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const { return false; } @@ -471,6 +512,20 @@ HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : InlineAttribute(name) { } +void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + } +} + +void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + HeightfieldDataPointer data = decodeInline(value); + if (data) { + data->write(out, true); + } + } +} + bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const { return false; } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 262ef89462..3ef37646d6 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -13,6 +13,7 @@ #define hifi_AttributeRegistry_h #include +#include #include #include #include @@ -424,9 +425,13 @@ public: const QByteArray& getContents() const { return _contents; } + void write(Bitstream& out, bool color); + private: QByteArray _contents; + QByteArray _encoded; + QMutex _encodedMutex; }; typedef QExplicitlySharedDataPointer HeightfieldDataPointer; @@ -439,6 +444,9 @@ public: Q_INVOKABLE HeightfieldAttribute(const QString& name = QString()); + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; @@ -450,6 +458,9 @@ public: Q_INVOKABLE HeightfieldColorAttribute(const QString& name = QString()); + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; From 82e442c11551acb0c83ff0ca0f4a787bfd3c9763 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 30 Jul 2014 14:54:38 -0700 Subject: [PATCH 07/13] Working on heightfield transmission, merging. --- interface/src/MetavoxelSystem.cpp | 65 ++++--- interface/src/MetavoxelSystem.h | 11 +- .../metavoxels/src/AttributeRegistry.cpp | 160 +++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 1 + libraries/metavoxels/src/Bitstream.cpp | 10 ++ libraries/metavoxels/src/Bitstream.h | 5 + 6 files changed, 220 insertions(+), 32 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e1479f8f02..d357ab5ce4 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -29,6 +29,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(StaticModelRenderer) +static int texturePointerMetaTypeId = qRegisterMetaType(); static int bufferPointVectorMetaTypeId = qRegisterMetaType(); 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(WHITE_COLOR)); + _colorTexture->setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, const_cast(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(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 89ea7dcbda..725ef9ca71 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -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 BufferPair; static QHash _bufferPairs; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index fefbbc3cd8..a255a9f8ef 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -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(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(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(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(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(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(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(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(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; } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 3ef37646d6..767ebf6527 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -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; } diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index f49ae1c04f..e9ac3d6319 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -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); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 7602424ded..1fd9205387 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -430,6 +430,11 @@ public: template void writeRawDelta(const QHash& value, const QHash& reference); template void readRawDelta(QHash& value, const QHash& 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); From a722c4ba0a1e371187cc3b6bbfa7c06f6ab5363d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 30 Jul 2014 15:22:45 -0700 Subject: [PATCH 08/13] Merge fix. --- .../metavoxels/src/AttributeRegistry.cpp | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index a255a9f8ef..499f8146cf 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -574,19 +574,14 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) 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; + uchar* src = (uchar*)childContents.data(); + int childSizePlusOne = childSize + 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); + for (char* end = dest + halfSize; dest != end; src += 2) { + *dest++ = yOffset + (qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])) >> 1); } dest += halfSize; - src0 += childSize; - src1 += childSize; - src2 += childSize; - src3 += childSize; + src += childSize; } } *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents)); @@ -655,23 +650,25 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post 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; + uchar* src = (uchar*)childContents.data(); int childStride = childSize * BYTES_PER_PIXEL; - uchar* src2 = src0 + childStride; - uchar* src3 = src2 + BYTES_PER_PIXEL; int halfStride = halfSize * BYTES_PER_PIXEL; + int childStep = 2 * BYTES_PER_PIXEL; + int redOffset3 = childStride + BYTES_PER_PIXEL; + int greenOffset1 = BYTES_PER_PIXEL + 1; + int greenOffset2 = childStride + 1; + int greenOffset3 = childStride + BYTES_PER_PIXEL + 1; + int blueOffset1 = BYTES_PER_PIXEL + 2; + int blueOffset2 = childStride + 2; + int blueOffset3 = childStride + BYTES_PER_PIXEL + 2; 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; + for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) { + *dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2; + *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; + *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; } dest += halfStride; - src0 += childStride; - src1 += childStride; - src2 += childStride; - src3 += childStride; + src += childStride; } } *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents)); From f356f0652b72eb4cb6ef88e44aa1666c3bc9aaca Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 30 Jul 2014 16:36:40 -0700 Subject: [PATCH 09/13] Collapse nodes when they fall out of LOD. --- libraries/metavoxels/src/MetavoxelData.cpp | 176 ++++++++++++--------- libraries/metavoxels/src/MetavoxelData.h | 6 + 2 files changed, 111 insertions(+), 71 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 29f18f5cc1..8860d30c93 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -54,6 +54,18 @@ bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, return true; } +bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float size, + const MetavoxelLOD& reference, float multiplier) const { + if (position == reference.position && threshold == reference.threshold) { + return false; // first off, nothing becomes subdivided or collapsed if it doesn't change + } + if (!(shouldSubdivide(minimum, size, multiplier) || reference.shouldSubdivide(minimum, size, multiplier))) { + return false; // this one or the reference must be subdivided + } + // TODO: find some way of culling subtrees that can't possibly contain subdivided or collapsed nodes + return true; +} + MetavoxelData::MetavoxelData() : _size(1.0f) { } @@ -567,53 +579,67 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD // shallow copy the reference *this = reference; + QHash remainingRoots = _roots; + glm::vec3 minimum = getMinimum(); + bool changed; in >> changed; - if (!changed) { - return; - } - - bool sizeChanged; - in >> sizeChanged; - if (sizeChanged) { - float size; - in >> size; - while (_size < size) { - expand(); - } - } - - forever { - AttributePointer attribute; - in >> attribute; - if (!attribute) { - break; - } - MetavoxelStreamBase base = { attribute, in, lod, referenceLOD }; - MetavoxelStreamState state = { base, getMinimum(), _size }; - MetavoxelNode* oldRoot = _roots.value(attribute); - if (oldRoot) { - bool changed; - in >> changed; - if (changed) { - oldRoot->incrementReferenceCount(); - attribute->readMetavoxelDelta(*this, *oldRoot, state); - oldRoot->decrementReferenceCount(attribute); - } else { - attribute->readMetavoxelSubdivision(*this, state); + if (changed) { + bool sizeChanged; + in >> sizeChanged; + if (sizeChanged) { + float size; + in >> size; + while (_size < size) { + expand(); } - } else { - attribute->readMetavoxelRoot(*this, state); - } + } + + forever { + AttributePointer attribute; + in >> attribute; + if (!attribute) { + break; + } + MetavoxelStreamBase base = { attribute, in, lod, referenceLOD }; + MetavoxelStreamState state = { base, minimum, _size }; + MetavoxelNode* oldRoot = _roots.value(attribute); + if (oldRoot) { + bool changed; + in >> changed; + if (changed) { + oldRoot->incrementReferenceCount(); + attribute->readMetavoxelDelta(*this, *oldRoot, state); + oldRoot->decrementReferenceCount(attribute); + } else { + attribute->readMetavoxelSubdivision(*this, state); + } + remainingRoots.remove(attribute); + + } else { + attribute->readMetavoxelRoot(*this, state); + } + } + + forever { + AttributePointer attribute; + in >> attribute; + if (!attribute) { + break; + } + _roots.take(attribute)->decrementReferenceCount(attribute); + remainingRoots.remove(attribute); + } } - forever { - AttributePointer attribute; - in >> attribute; - if (!attribute) { - break; + // read subdivisions for the remaining roots if there's any chance of a collapse + if (!(lod.position == referenceLOD.position && lod.threshold <= referenceLOD.threshold)) { + for (QHash::const_iterator it = remainingRoots.constBegin(); + it != remainingRoots.constEnd(); it++) { + MetavoxelStreamBase base = { it.key(), in, lod, referenceLOD }; + MetavoxelStreamState state = { base, minimum, _size }; + it.key()->readMetavoxelSubdivision(*this, state); } - _roots.take(attribute)->decrementReferenceCount(attribute); } } @@ -788,6 +814,10 @@ bool MetavoxelStreamState::becameSubdivided() const { return base.lod.becameSubdivided(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier()); } +bool MetavoxelStreamState::becameSubdividedOrCollapsed() const { + return base.lod.becameSubdividedOrCollapsed(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier()); +} + void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) { minimum = getNextMinimum(lastMinimum, size, index); } @@ -923,7 +953,7 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta _children[i] = new MetavoxelNode(state.base.attribute); _children[i]->readDelta(*reference._children[i], nextState); } else { - if (nextState.becameSubdivided()) { + if (nextState.becameSubdividedOrCollapsed()) { _children[i] = reference._children[i]->readSubdivision(nextState); if (_children[i] == reference._children[i]) { _children[i]->incrementReferenceCount(); @@ -972,42 +1002,46 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt } MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { - if (!state.shouldSubdivideReference()) { - bool leaf; - state.base.stream >> leaf; - if (leaf) { - return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.base.attribute)); - - } else { - MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.base.attribute)); + if (state.shouldSubdivide()) { + if (!state.shouldSubdivideReference()) { + bool leaf; + state.base.stream >> leaf; + if (leaf) { + return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.base.attribute)); + + } else { + MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.base.attribute)); + MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + newNode->_children[i] = new MetavoxelNode(state.base.attribute); + newNode->_children[i]->read(nextState); + } + return newNode; + } + } else if (!isLeaf()) { + MetavoxelNode* node = this; MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); - newNode->_children[i] = new MetavoxelNode(state.base.attribute); - newNode->_children[i]->read(nextState); - } - return newNode; - } - } else if (!isLeaf()) { - MetavoxelNode* node = this; - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdivided()) { - MetavoxelNode* child = _children[i]->readSubdivision(nextState); - if (child != _children[i]) { - if (node == this) { - node = new MetavoxelNode(state.base.attribute, this); + if (nextState.becameSubdividedOrCollapsed()) { + MetavoxelNode* child = _children[i]->readSubdivision(nextState); + if (child != _children[i]) { + if (node == this) { + node = new MetavoxelNode(state.base.attribute, this); + } + node->_children[i] = child; + _children[i]->decrementReferenceCount(state.base.attribute); } - node->_children[i] = child; - _children[i]->decrementReferenceCount(state.base.attribute); } } + if (node != this) { + node->mergeChildren(state.base.attribute, true); + } + return node; } - if (node != this) { - node->mergeChildren(state.base.attribute, true); - } - return node; + } else if (!isLeaf()) { + return new MetavoxelNode(getAttributeValue(state.base.attribute)); } return this; } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 46376601c1..24eb09763c 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -55,6 +55,11 @@ public: /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const; + + /// Checks whether the node or any of the nodes underneath it have had subdivision + /// enabled or disabled as compared to the reference. + bool becameSubdividedOrCollapsed(const glm::vec3& minimum, float size, + const MetavoxelLOD& reference, float multiplier = 1.0f) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) @@ -181,6 +186,7 @@ public: bool shouldSubdivide() const; bool shouldSubdivideReference() const; bool becameSubdivided() const; + bool becameSubdividedOrCollapsed() const; void setMinimum(const glm::vec3& lastMinimum, int index); }; From 6c15468c87b83c95bfe6c83162ac5a4ca0353cd9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 30 Jul 2014 17:21:31 -0700 Subject: [PATCH 10/13] Fix for sizing. --- libraries/metavoxels/src/MetavoxelData.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 8860d30c93..a0b1f1efb0 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -580,8 +580,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD *this = reference; QHash remainingRoots = _roots; - glm::vec3 minimum = getMinimum(); - + bool changed; in >> changed; if (changed) { @@ -594,7 +593,8 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD expand(); } } - + + glm::vec3 minimum = getMinimum(); forever { AttributePointer attribute; in >> attribute; @@ -634,6 +634,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD // read subdivisions for the remaining roots if there's any chance of a collapse if (!(lod.position == referenceLOD.position && lod.threshold <= referenceLOD.threshold)) { + glm::vec3 minimum = getMinimum(); for (QHash::const_iterator it = remainingRoots.constBegin(); it != remainingRoots.constEnd(); it++) { MetavoxelStreamBase base = { it.key(), in, lod, referenceLOD }; From c3c2265ddd3e89ae3f66596f82c553b4fc085bd2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 30 Jul 2014 18:14:11 -0700 Subject: [PATCH 11/13] Hide the unset (black) bits. --- .../resources/shaders/metavoxel_heightfield.vert | 10 +++++----- interface/src/MetavoxelSystem.cpp | 15 +++++++++++++-- libraries/metavoxels/src/AttributeRegistry.cpp | 3 ++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/interface/resources/shaders/metavoxel_heightfield.vert b/interface/resources/shaders/metavoxel_heightfield.vert index a5e7ec66c1..3cf43e87cd 100644 --- a/interface/resources/shaders/metavoxel_heightfield.vert +++ b/interface/resources/shaders/metavoxel_heightfield.vert @@ -21,13 +21,13 @@ void main(void) { // transform and store the normal for interpolation normal = normalize(gl_ModelViewMatrix * vec4(0.0, 1.0, 0.0, 0.0)); - // pass along the vertex color - gl_FrontColor = gl_Color; - // pass along the texture coordinates gl_TexCoord[0] = gl_MultiTexCoord0; // add the height to the position - gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + - vec4(0.0, texture2D(heightMap, gl_MultiTexCoord0.st).r, 0.0, 0.0)); + float height = texture2D(heightMap, gl_MultiTexCoord0.st).r; + gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); + + // the zero height should be invisible + gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); } diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d357ab5ce4..16ce98c65c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -397,7 +397,11 @@ void HeightfieldBuffer::render() { QHash HeightfieldBuffer::_bufferPairs; void HeightfieldPreview::render(const glm::vec3& translation, float scale) const { - glColor4f(1.0f, 1.0f, 1.0f, 0.75f); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -418,6 +422,9 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); } BufferDataAttribute::BufferDataAttribute(const QString& name) : @@ -695,7 +702,10 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox _pointProgram.release(); - glColor4f(1.0f, 1.0f, 1.0f, 0.0f); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); _heightfieldProgram.bind(); @@ -710,6 +720,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); + glDisable(GL_ALPHA_TEST); glEnable(GL_BLEND); } diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 499f8146cf..7219e9da41 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -578,7 +578,8 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) int childSizePlusOne = childSize + 1; for (int z = 0; z < halfSize; z++) { for (char* end = dest + halfSize; dest != end; src += 2) { - *dest++ = yOffset + (qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])) >> 1); + int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); + *dest++ = (max == 0) ? 0 : (yOffset + (max >> 1)); } dest += halfSize; src += childSize; From 90ce73823a058ee3ed4d60855d1f335b9c14e95f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 31 Jul 2014 11:03:50 -0700 Subject: [PATCH 12/13] Attempting to fix the Linux build error. --- interface/src/MetavoxelSystem.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 725ef9ca71..2258be6e87 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -12,6 +12,9 @@ #ifndef hifi_MetavoxelSystem_h #define hifi_MetavoxelSystem_h +// include this before QOpenGLTexture, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include #include #include From a0886d430127995329160b6416200f5b4da06a87 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 31 Jul 2014 11:55:52 -0700 Subject: [PATCH 13/13] Use raw OpenGL textures rather than QOpenGLTexture, since that we're having issues with constants in that class. --- interface/src/MetavoxelSystem.cpp | 79 ++++++++++++++++--------------- interface/src/MetavoxelSystem.h | 15 ++---- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 16ce98c65c..cff5497183 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -29,7 +29,6 @@ REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(StaticModelRenderer) -static int texturePointerMetaTypeId = qRegisterMetaType(); static int bufferPointVectorMetaTypeId = qRegisterMetaType(); void MetavoxelSystem::init() { @@ -113,9 +112,9 @@ void MetavoxelSystem::render() { guideToAugmented(renderVisitor); } -void MetavoxelSystem::deleteTextures(const TexturePointer& height, const TexturePointer& color) { - delete height; - delete color; +void MetavoxelSystem::deleteTextures(int heightID, int colorID) { + glDeleteTextures(1, (GLuint*)&heightID); + glDeleteTextures(1, (GLuint*)&colorID); } MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { @@ -267,18 +266,19 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _height(height), _color(color), _clearAfterLoading(clearAfterLoading), - _heightTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)), - _colorTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)) { + _heightTextureID(0), + _colorTextureID(0), + _heightSize(glm::sqrt(height.size())) { } 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)); + Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID)); } else { - delete _heightTexture; - delete _colorTexture; + glDeleteTextures(1, &_heightTextureID); + glDeleteTextures(1, &_colorTextureID); } } @@ -290,49 +290,46 @@ public: void HeightfieldBuffer::render() { // initialize textures, etc. on first render - 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()); + if (_heightTextureID == 0) { + glGenTextures(1, &_heightTextureID); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData()); if (_clearAfterLoading) { _height.clear(); } - if (!_color.isEmpty()) { + + glGenTextures(1, &_colorTextureID); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (_color.isEmpty()) { + const quint8 WHITE_COLOR[] = { 255, 255, 255 }; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); + + } else { int colorSize = glm::sqrt(_color.size() / 3); - _colorTexture->setSize(colorSize, colorSize); - } - _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()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); if (_clearAfterLoading) { _color.clear(); } - } else { - const quint8 WHITE_COLOR[] = { 255, 255, 255 }; - _colorTexture->setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, const_cast(WHITE_COLOR)); } } // create the buffer objects lazily - int size = _heightTexture->width(); - int sizeWithSkirt = size + 2; + int sizeWithSkirt = _heightSize + 2; int vertexCount = sizeWithSkirt * sizeWithSkirt; int rows = sizeWithSkirt - 1; int indexCount = rows * rows * 4; - BufferPair& bufferPair = _bufferPairs[size]; + BufferPair& bufferPair = _bufferPairs[_heightSize]; if (!bufferPair.first.isCreated()) { QVector vertices(vertexCount); HeightfieldPoint* point = vertices.data(); - float step = 1.0f / (size - 1); + float step = 1.0f / (_heightSize - 1); float z = -step; for (int i = 0; i < sizeWithSkirt; i++, z += step) { float x = -step; @@ -380,13 +377,17 @@ void HeightfieldBuffer::render() { glTranslatef(_translation.x, _translation.y, _translation.z); glScalef(_scale, _scale, _scale); - _heightTexture->bind(0); - _colorTexture->bind(1); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - _colorTexture->release(1); - _heightTexture->release(0); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); glPopMatrix(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 2258be6e87..bd05fc0f67 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -12,12 +12,8 @@ #ifndef hifi_MetavoxelSystem_h #define hifi_MetavoxelSystem_h -// include this before QOpenGLTexture, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - #include #include -#include #include #include @@ -29,8 +25,6 @@ class Model; -typedef QOpenGLTexture* TexturePointer; - /// Renders a metavoxel tree. class MetavoxelSystem : public MetavoxelClientManager { Q_OBJECT @@ -47,7 +41,7 @@ public: void simulate(float deltaTime); void render(); - Q_INVOKABLE void deleteTextures(const TexturePointer& height, const TexturePointer& color); + Q_INVOKABLE void deleteTextures(int heightID, int colorID); protected: @@ -64,8 +58,6 @@ private: QReadWriteLock _lodLock; }; -Q_DECLARE_METATYPE(TexturePointer) - /// Describes contents of a point in a point buffer. class BufferPoint { public: @@ -154,8 +146,9 @@ private: QByteArray _height; QByteArray _color; bool _clearAfterLoading; - TexturePointer _heightTexture; - TexturePointer _colorTexture; + GLuint _heightTextureID; + GLuint _colorTextureID; + int _heightSize; typedef QPair BufferPair; static QHash _bufferPairs;