From e08a0144c27a12154be6c0204ba92de7d85fa72b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Nov 2014 19:15:43 -0800 Subject: [PATCH 01/38] Cleanup on aisle three! Removed the color/normal/voxelized spanner bits, as well as the throbbing/scripted guides and the notion of spanner masking. --- .../resources/shaders/metavoxel_point.vert | 31 -- interface/src/MetavoxelSystem.cpp | 234 +---------- interface/src/MetavoxelSystem.h | 72 +--- interface/src/ui/MetavoxelEditor.cpp | 243 +----------- interface/src/ui/MetavoxelEditor.h | 15 - .../metavoxels/src/AttributeRegistry.cpp | 220 +---------- libraries/metavoxels/src/AttributeRegistry.h | 100 +---- .../metavoxels/src/MetavoxelClientManager.cpp | 13 - .../metavoxels/src/MetavoxelClientManager.h | 4 - libraries/metavoxels/src/MetavoxelData.cpp | 363 ++---------------- libraries/metavoxels/src/MetavoxelData.h | 115 +----- .../metavoxels/src/MetavoxelMessages.cpp | 180 +-------- libraries/metavoxels/src/MetavoxelMessages.h | 15 - tests/metavoxels/src/MetavoxelTests.cpp | 17 +- 14 files changed, 75 insertions(+), 1547 deletions(-) delete mode 100644 interface/resources/shaders/metavoxel_point.vert diff --git a/interface/resources/shaders/metavoxel_point.vert b/interface/resources/shaders/metavoxel_point.vert deleted file mode 100644 index 02de2bfa01..0000000000 --- a/interface/resources/shaders/metavoxel_point.vert +++ /dev/null @@ -1,31 +0,0 @@ -#version 120 - -// -// metavoxel_point.vert -// vertex shader -// -// Created by Andrzej Kapolka on 12/12/13. -// Copyright 2013 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 -// - -uniform float pointScale; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // transform and store the normal for interpolation - normal = vec4(normalize(gl_NormalMatrix * gl_Normal), 0.0); - - // extract the first three components of the vertex for position - gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0); - - // the final component is the size in world space - gl_PointSize = pointScale * gl_Vertex.w / gl_Position.w; - - // copy the color for interpolation - gl_FrontColor = vec4(gl_Color.rgb, 0.0); -} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4c0acc553a..a671452143 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -37,8 +37,6 @@ REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(CuboidRenderer) REGISTER_META_OBJECT(StaticModelRenderer) -static int bufferPointVectorMetaTypeId = qRegisterMetaType(); - MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, int minimumDelay, int maximumDelay, int bandwidthLimit) : dropRate(dropRate), @@ -59,8 +57,6 @@ void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); - _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); - _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("heightfieldBuffer")); _heightfieldBufferAttribute->setLODThresholdMultiplier( @@ -920,36 +916,6 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { BufferData::~BufferData() { } -PointBuffer::PointBuffer(const BufferPointVector& points) : - _points(points) { -} - -void PointBuffer::render(bool cursor) { - // initialize buffer, etc. on first render - if (!_buffer.isCreated()) { - _buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); - _buffer.create(); - _buffer.bind(); - _pointCount = _points.size(); - _buffer.allocate(_points.constData(), _pointCount * sizeof(BufferPoint)); - _points.clear(); - _buffer.release(); - } - if (_pointCount == 0) { - return; - } - _buffer.bind(); - - BufferPoint* point = 0; - glVertexPointer(4, GL_FLOAT, sizeof(BufferPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(BufferPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(BufferPoint), &point->normal); - - glDrawArrays(GL_POINTS, 0, _pointCount); - - _buffer.release(); -} - const int HeightfieldBuffer::HEIGHT_BORDER = 1; const int HeightfieldBuffer::SHARED_EDGE = 1; const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE; @@ -1576,17 +1542,7 @@ AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) c } void DefaultMetavoxelRendererImplementation::init() { - if (!_pointProgram.isLinked()) { - _pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_point.vert"); - _pointProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_voxel_base.frag"); - _pointProgram.link(); - - _pointProgram.bind(); - _pointScaleLocation = _pointProgram.uniformLocation("pointScale"); - _pointProgram.release(); - + if (!_baseHeightfieldProgram.isLinked()) { _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_heightfield_base.vert"); _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + @@ -1631,76 +1587,6 @@ void DefaultMetavoxelRendererImplementation::init() { DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } -class PointAugmentVisitor : public MetavoxelVisitor { -public: - - PointAugmentVisitor(const MetavoxelLOD& lod); - - virtual void prepare(MetavoxelData* data); - virtual int visit(MetavoxelInfo& info); - virtual bool postVisit(MetavoxelInfo& info); - -private: - - BufferPointVector _points; - float _pointLeafSize; -}; - -PointAugmentVisitor::PointAugmentVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod) { -} - -const int ALPHA_RENDER_THRESHOLD = 0; - -void PointAugmentVisitor::prepare(MetavoxelData* data) { - MetavoxelVisitor::prepare(data); - const float MAX_POINT_LEAF_SIZE = 64.0f; - _pointLeafSize = qMin(data->getSize(), MAX_POINT_LEAF_SIZE); -} - -int PointAugmentVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return (info.size > _pointLeafSize) ? DEFAULT_ORDER : (DEFAULT_ORDER | ALL_NODES_REST); - } - QRgb color = info.inputValues.at(0).getInlineValue(); - quint8 alpha = qAlpha(color); - if (alpha > ALPHA_RENDER_THRESHOLD) { - QRgb normal = info.inputValues.at(1).getInlineValue(); - BufferPoint point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), - { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)) }, - { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; - _points.append(point); - } - if (info.size >= _pointLeafSize) { - PointBuffer* buffer = NULL; - if (!_points.isEmpty()) { - BufferPointVector swapPoints; - _points.swap(swapPoints); - buffer = new PointBuffer(swapPoints); - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - } - return STOP_RECURSION; -} - -bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { - if (info.size != _pointLeafSize) { - return false; - } - PointBuffer* buffer = NULL; - if (!_points.isEmpty()) { - BufferPointVector swapPoints; - _points.swap(swapPoints); - buffer = new PointBuffer(swapPoints); - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return true; -} - class HeightfieldFetchVisitor : public MetavoxelVisitor { public: @@ -2749,15 +2635,9 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const while (expandedPrevious.getSize() < data.getSize()) { expandedPrevious.expand(); } - const AttributePointer& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute(); - MetavoxelNode* root = expandedPrevious.getRoot(pointBufferAttribute); - if (root) { - data.setRoot(pointBufferAttribute, root); - root->incrementReferenceCount(); - } const AttributePointer& heightfieldBufferAttribute = Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(); - root = expandedPrevious.getRoot(heightfieldBufferAttribute); + MetavoxelNode* root = expandedPrevious.getRoot(heightfieldBufferAttribute); if (root) { data.setRoot(heightfieldBufferAttribute, root); root->incrementReferenceCount(); @@ -2769,10 +2649,6 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } - - PointAugmentVisitor pointAugmentVisitor(lod); - data.guideToDifferent(expandedPrevious, pointAugmentVisitor); - HeightfieldRegionVisitor heightfieldRegionVisitor(lod); data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor); @@ -2789,7 +2665,7 @@ public: SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + virtual bool visit(Spanner* spanner); private: @@ -2798,11 +2674,11 @@ private: SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), QVector(), lod), + QVector(), QVector(), lod), _deltaTime(deltaTime) { } -bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { +bool SpannerSimulateVisitor::visit(Spanner* spanner) { spanner->getRenderer()->simulate(_deltaTime); return true; } @@ -2819,7 +2695,7 @@ public: SpannerRenderVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + virtual bool visit(Spanner* spanner); private: @@ -2828,8 +2704,8 @@ private: SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), QVector(), - lod, encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), + QVector(), QVector(), lod, + encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } @@ -2845,9 +2721,9 @@ int SpannerRenderVisitor::visit(MetavoxelInfo& info) { return SpannerVisitor::visit(info); } -bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { +bool SpannerRenderVisitor::visit(Spanner* spanner) { const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); - spanner->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); + spanner->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DEFAULT_MODE); return true; } @@ -2893,40 +2769,10 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox SpannerRenderVisitor spannerRenderVisitor(lod); data.guide(spannerRenderVisitor); - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - const int VIEWPORT_WIDTH_INDEX = 2; - const int VIEWPORT_HEIGHT_INDEX = 3; - float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; - float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; - float viewportDiagonal = sqrtf(viewportWidth * viewportWidth + viewportHeight * viewportHeight); - float worldDiagonal = glm::distance(Application::getInstance()->getDisplayViewFrustum()->getNearBottomLeft(), - Application::getInstance()->getDisplayViewFrustum()->getNearTopRight()); - - _pointProgram.bind(); - _pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal * - Application::getInstance()->getDisplayViewFrustum()->getNearClip() / worldDiagonal); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - - glDisable(GL_BLEND); - - BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute()); - data.guide(pointRenderVisitor); - - glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - - _pointProgram.release(); - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + glEnableClientState(GL_VERTEX_ARRAY); + glDisable(GL_BLEND); glEnable(GL_CULL_FACE); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_EQUAL, 0.0f); @@ -2994,8 +2840,6 @@ void DefaultMetavoxelRendererImplementation::loadSplatProgram(const char* type, program.release(); } -ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; -int DefaultMetavoxelRendererImplementation::_pointScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram; int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation; int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation; @@ -3007,55 +2851,10 @@ ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram; DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations; ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram; -static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { - GLdouble coefficients[] = { x, y, z, w }; - glClipPlane(plane, coefficients); - glEnable(plane); -} - -void ClippedRenderer::render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize) { - if (clipSize == 0.0f) { - renderUnclipped(color, mode); - return; - } - enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize); - enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); - enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipSize); - enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); - enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize); - enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z); - - renderUnclipped(color, mode); - - glDisable(GL_CLIP_PLANE0); - glDisable(GL_CLIP_PLANE1); - glDisable(GL_CLIP_PLANE2); - glDisable(GL_CLIP_PLANE3); - glDisable(GL_CLIP_PLANE4); - glDisable(GL_CLIP_PLANE5); -} - SphereRenderer::SphereRenderer() { } -void SphereRenderer::render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize) { - if (clipSize == 0.0f) { - renderUnclipped(color, mode); - return; - } - // slight performance optimization: don't render if clip bounds are entirely within sphere - Sphere* sphere = static_cast(_spanner); - Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); - for (int i = 0; i < Box::VERTEX_COUNT; i++) { - const float CLIP_PROPORTION = 0.95f; - if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) { - ClippedRenderer::render(color, mode, clipMinimum, clipSize); - return; - } - } -} - -void SphereRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { +void SphereRenderer::render(const glm::vec4& color, Mode mode) { Sphere* sphere = static_cast(_spanner); const QColor& ownColor = sphere->getColor(); glColor4f(ownColor.redF() * color.r, ownColor.greenF() * color.g, ownColor.blueF() * color.b, ownColor.alphaF() * color.a); @@ -3075,7 +2874,7 @@ void SphereRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { CuboidRenderer::CuboidRenderer() { } -void CuboidRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { +void CuboidRenderer::render(const glm::vec4& color, Mode mode) { Cuboid* cuboid = static_cast(_spanner); const QColor& ownColor = cuboid->getColor(); glColor4f(ownColor.redF() * color.r, ownColor.greenF() * color.g, ownColor.blueF() * color.b, ownColor.alphaF() * color.a); @@ -3126,7 +2925,7 @@ void StaticModelRenderer::simulate(float deltaTime) { _model->simulate(deltaTime); } -void StaticModelRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { +void StaticModelRenderer::render(const glm::vec4& color, Mode mode) { switch (mode) { case DIFFUSE_MODE: _model->render(color.a, Model::DIFFUSE_RENDER_MODE); @@ -3143,8 +2942,7 @@ void StaticModelRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { _model->render(color.a); } -bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { +bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { RayIntersectionInfo info; info._rayStart = origin; info._rayDirection = direction; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7e9f2ce2ca..e0fce70c2d 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -54,7 +54,6 @@ public: void setNetworkSimulation(const NetworkSimulation& simulation); NetworkSimulation getNetworkSimulation(); - const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } @@ -103,7 +102,6 @@ private: void guideToAugmented(MetavoxelVisitor& visitor, bool render = false); - AttributePointer _pointBufferAttribute; AttributePointer _heightfieldBufferAttribute; AttributePointer _voxelBufferAttribute; @@ -124,18 +122,6 @@ public slots: virtual void handle() = 0; }; -/// Describes contents of a point in a point buffer. -class BufferPoint { -public: - glm::vec4 vertex; - quint8 color[3]; - quint8 normal[3]; -}; - -typedef QVector BufferPointVector; - -Q_DECLARE_METATYPE(BufferPointVector) - /// Simple throttle for limiting bandwidth on a per-second basis. class Throttle { public: @@ -203,21 +189,6 @@ public: typedef QExplicitlySharedDataPointer BufferDataPointer; -/// Contains the information necessary to render a group of points. -class PointBuffer : public BufferData { -public: - - PointBuffer(const BufferPointVector& points); - - virtual void render(bool cursor = false); - -private: - - BufferPointVector _points; - QOpenGLBuffer _buffer; - int _pointCount; -}; - /// Contains the information necessary to render a heightfield block. class HeightfieldBuffer : public BufferData { public: @@ -369,9 +340,6 @@ public: static void init(); - static ProgramObject& getPointProgram() { return _pointProgram; } - static int getPointScaleLocation() { return _pointScaleLocation; } - static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; } static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; } static int getBaseColorScaleLocation() { return _baseColorScaleLocation; } @@ -411,9 +379,6 @@ private: static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); - static ProgramObject _pointProgram; - static int _pointScaleLocation; - static ProgramObject _baseHeightfieldProgram; static int _baseHeightScaleLocation; static int _baseColorScaleLocation; @@ -438,49 +403,30 @@ private: static ProgramObject _voxelCursorProgram; }; -/// Base class for spanner renderers; provides clipping. -class ClippedRenderer : public SpannerRenderer { - Q_OBJECT - -public: - - virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize); - -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode) = 0; -}; - /// Renders spheres. -class SphereRenderer : public ClippedRenderer { +class SphereRenderer : public SpannerRenderer { Q_OBJECT public: Q_INVOKABLE SphereRenderer(); - virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize); - -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode); + virtual void render(const glm::vec4& color, Mode mode); }; /// Renders cuboids. -class CuboidRenderer : public ClippedRenderer { +class CuboidRenderer : public SpannerRenderer { Q_OBJECT public: Q_INVOKABLE CuboidRenderer(); -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode); + virtual void render(const glm::vec4& color, Mode mode); }; /// Renders static models. -class StaticModelRenderer : public ClippedRenderer { +class StaticModelRenderer : public SpannerRenderer { Q_OBJECT public: @@ -489,12 +435,8 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; - -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode); + virtual void render(const glm::vec4& color, Mode mode); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; private slots: diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index e5a1f7f7a4..2b3b0a3870 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -122,7 +122,6 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new InsertSpannerTool(this)); addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); - addTool(new SetSpannerTool(this)); addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldMaterialBrushTool(this)); addTool(new ImportHeightfieldTool(this)); @@ -241,7 +240,7 @@ void MetavoxelEditor::createNewAttribute() { form.addRow("Name:", &name); SharedObjectEditor editor(&Attribute::staticMetaObject, false); - editor.setObject(new QRgbAttribute()); + editor.setObject(new FloatAttribute()); layout.addWidget(&editor); QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -622,7 +621,7 @@ void PlaceSpannerTool::render() { const float SPANNER_ALPHA = 0.25f; QColor color = getColor(); spanner->getRenderer()->render(glm::vec4(color.redF(), color.greenF(), color.blueF(), SPANNER_ALPHA), - SpannerRenderer::DEFAULT_MODE, glm::vec3(), 0.0f); + SpannerRenderer::DEFAULT_MODE); } bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { @@ -712,244 +711,6 @@ void ClearSpannersTool::clear() { Application::getInstance()->getMetavoxels()->applyEdit(message); } -SetSpannerTool::SetSpannerTool(MetavoxelEditor* editor) : - PlaceSpannerTool(editor, "Set Spanner", "Set") { -} - -bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute == AttributeRegistry::getInstance()->getSpannersAttribute(); -} - -glm::quat DIRECTION_ROTATIONS[] = { - rotationBetween(glm::vec3(-1.0f, 0.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(1.0f, 0.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, -1.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, 1.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, 0.0f, 1.0f), IDENTITY_FRONT) }; - -/// Represents a view from one direction of the spanner to be voxelized. -class DirectionImages { -public: - QImage color; - QVector depth; - glm::vec3 minima; - glm::vec3 maxima; - glm::vec3 scale; -}; - -class Voxelizer : public QRunnable { -public: - - Voxelizer(float size, const Box& bounds, float granularity, const QVector& directionImages); - - virtual void run(); - -private: - - void voxelize(const glm::vec3& center); - - float _size; - Box _bounds; - float _granularity; - QVector _directionImages; -}; - -Voxelizer::Voxelizer(float size, const Box& bounds, float granularity, const QVector& directionImages) : - _size(size), - _bounds(bounds), - _granularity(granularity), - _directionImages(directionImages) { -} - -void Voxelizer::run() { - // voxelize separately each cell within the bounds - float halfSize = _size * 0.5f; - for (float x = _bounds.minimum.x + halfSize; x < _bounds.maximum.x; x += _size) { - for (float y = _bounds.minimum.y + halfSize; y < _bounds.maximum.y; y += _size) { - for (float z = _bounds.minimum.z + halfSize; z < _bounds.maximum.z; z += _size) { - voxelize(glm::vec3(x, y, z)); - } - } - } -} - -class VoxelizationVisitor : public MetavoxelVisitor { -public: - - VoxelizationVisitor(const QVector& directionImages, const glm::vec3& center, float granularity); - - virtual int visit(MetavoxelInfo& info); - -private: - - QVector _directionImages; - glm::vec3 _center; - float _granularity; -}; - -VoxelizationVisitor::VoxelizationVisitor(const QVector& directionImages, - const glm::vec3& center, float granularity) : - MetavoxelVisitor(QVector(), QVector() << - AttributeRegistry::getInstance()->getColorAttribute()), - _directionImages(directionImages), - _center(center), - _granularity(granularity) { -} - -bool checkDisjoint(const DirectionImages& images, const glm::vec3& minimum, const glm::vec3& maximum, float extent) { - for (int x = qMax(0, (int)minimum.x), xmax = qMin(images.color.width(), (int)maximum.x); x < xmax; x++) { - for (int y = qMax(0, (int)minimum.y), ymax = qMin(images.color.height(), (int)maximum.y); y < ymax; y++) { - float depth = 1.0f - images.depth.at(y * images.color.width() + x); - if (depth - minimum.z >= -extent - EPSILON) { - return false; - } - } - } - return true; -} - -int VoxelizationVisitor::visit(MetavoxelInfo& info) { - float halfSize = info.size * 0.5f; - glm::vec3 center = info.minimum + _center + glm::vec3(halfSize, halfSize, halfSize); - const float EXTENT_SCALE = 2.0f; - if (info.size > _granularity) { - for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) { - glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center; - const DirectionImages& images = _directionImages.at(i); - glm::vec3 relative = (rotated - images.minima) * images.scale; - glm::vec3 extents = images.scale * halfSize; - glm::vec3 minimum = relative - extents; - glm::vec3 maximum = relative + extents; - if (checkDisjoint(images, minimum, maximum, extents.z * EXTENT_SCALE)) { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - return STOP_RECURSION; - } - } - return DEFAULT_ORDER; - } - QRgb closestColor = QRgb(); - float closestDistance = FLT_MAX; - for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) { - glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center; - const DirectionImages& images = _directionImages.at(i); - glm::vec3 relative = (rotated - images.minima) * images.scale; - int x = qMax(qMin((int)glm::round(relative.x), images.color.width() - 1), 0); - int y = qMax(qMin((int)glm::round(relative.y), images.color.height() - 1), 0); - float depth = 1.0f - images.depth.at(y * images.color.width() + x); - float distance = depth - relative.z; - float extent = images.scale.z * halfSize * EXTENT_SCALE; - if (distance < -extent - EPSILON) { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - return STOP_RECURSION; - } - QRgb color = images.color.pixel(x, y); - if (distance < extent + EPSILON) { - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(color)); - return STOP_RECURSION; - } - if (distance < closestDistance) { - closestColor = color; - closestDistance = distance; - } - } - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(closestColor)); - return STOP_RECURSION; -} - -void Voxelizer::voxelize(const glm::vec3& center) { - MetavoxelData data; - data.setSize(_size); - - VoxelizationVisitor visitor(_directionImages, center, _granularity); - data.guide(visitor); - - MetavoxelEditMessage edit = { QVariant::fromValue(SetDataEdit( - center - glm::vec3(_size, _size, _size) * 0.5f, data, true)) }; - QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "applyEdit", - Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, true)); -} - -void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { - Spanner* spannerData = static_cast(spanner.data()); - Box bounds = spannerData->getBounds(); - float longestSide(qMax(bounds.getLongestSide(), spannerData->getPlacementGranularity())); - float size = powf(2.0f, floorf(logf(longestSide) / logf(2.0f))); - Box cellBounds(glm::floor(bounds.minimum / size) * size, glm::ceil(bounds.maximum / size) * size); - - Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->bind(); - - glEnable(GL_SCISSOR_TEST); - glEnable(GL_LIGHTING); - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - - QVector directionImages; - - for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) { - glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX); - glm::vec3 maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int j = 0; j < Box::VERTEX_COUNT; j++) { - glm::vec3 rotated = DIRECTION_ROTATIONS[i] * cellBounds.getVertex(j); - minima = glm::min(minima, rotated); - maxima = glm::max(maxima, rotated); - } - float renderGranularity = spannerData->getVoxelizationGranularity() / 4.0f; - int width = glm::round((maxima.x - minima.x) / renderGranularity); - int height = glm::round((maxima.y - minima.y) / renderGranularity); - - glViewport(0, 0, width, height); - glScissor(0, 0, width, height); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glLoadIdentity(); - glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glm::vec3 axis = glm::axis(DIRECTION_ROTATIONS[i]); - glRotatef(glm::degrees(glm::angle(DIRECTION_ROTATIONS[i])), axis.x, axis.y, axis.z); - - Application::getInstance()->setupWorldLight(); - - Application::getInstance()->updateUntranslatedViewMatrix(); - - const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); - spannerData->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DIFFUSE_MODE, glm::vec3(), 0.0f); - - DirectionImages images = { QImage(width, height, QImage::Format_ARGB32), - QVector(width * height), minima, maxima, glm::vec3(width / (maxima.x - minima.x), - height / (maxima.y - minima.y), 1.0f / (maxima.z - minima.z)) }; - glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, images.color.bits()); - glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, images.depth.data()); - directionImages.append(images); - - glMatrixMode(GL_PROJECTION); - } - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - glEnable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - - Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->release(); - - glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(), - Application::getInstance()->getGLWidget()->getDeviceHeight()); - - // send the images off to the lab for processing - QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds, - spannerData->getVoxelizationGranularity(), directionImages)); -} - HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : MetavoxelTool(editor, name, false) { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index d17834ae18..5607439807 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -242,21 +242,6 @@ private slots: void clear(); }; -/// Allows setting the value by placing a spanner. -class SetSpannerTool : public PlaceSpannerTool { - Q_OBJECT - -public: - - SetSpannerTool(MetavoxelEditor* editor); - - virtual bool appliesTo(const AttributePointer& attribute) const; - -protected: - - virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); -}; - /// Base class for heightfield tools. class HeightfieldTool : public MetavoxelTool { Q_OBJECT diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c06e376a25..0ed20386c6 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -19,10 +19,6 @@ #include "MetavoxelData.h" REGISTER_META_OBJECT(FloatAttribute) -REGISTER_META_OBJECT(QRgbAttribute) -REGISTER_META_OBJECT(PackedNormalAttribute) -REGISTER_META_OBJECT(SpannerQRgbAttribute) -REGISTER_META_OBJECT(SpannerPackedNormalAttribute) REGISTER_META_OBJECT(MaterialObject) REGISTER_META_OBJECT(HeightfieldAttribute) REGISTER_META_OBJECT(HeightfieldColorAttribute) @@ -48,11 +44,6 @@ AttributeRegistry::AttributeRegistry() : _rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject, 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"))), _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))), @@ -89,8 +80,6 @@ static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engin void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) { QScriptValue registry = engine->newObject(); - registry.setProperty("colorAttribute", engine->newQObject(_colorAttribute.data())); - registry.setProperty("normalAttribute", engine->newQObject(_normalAttribute.data())); registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1)); engine->globalObject().setProperty("AttributeRegistry", registry); engine->globalObject().setProperty("qDebug", engine->newFunction(qDebugFunction, 1)); @@ -292,111 +281,8 @@ MetavoxelNode* Attribute::expandMetavoxelRoot(const MetavoxelNode& root) { return newParent; } -FloatAttribute::FloatAttribute(const QString& name, float defaultValue) : - SimpleInlineAttribute(name, defaultValue) { -} - -QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : - InlineAttribute(name, defaultValue) { -} - -bool QRgbAttribute::merge(void*& parent, void* children[], bool postRead) const { - QRgb firstValue = decodeInline(children[0]); - int totalAlpha = qAlpha(firstValue); - int totalRed = qRed(firstValue) * totalAlpha; - int totalGreen = qGreen(firstValue) * totalAlpha; - int totalBlue = qBlue(firstValue) * totalAlpha; - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - totalRed += qRed(value) * alpha; - totalGreen += qGreen(value) * alpha; - totalBlue += qBlue(value) * alpha; - totalAlpha += alpha; - allChildrenEqual &= (firstValue == value); - } - if (totalAlpha == 0) { - parent = encodeInline(QRgb()); - } else { - parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, - totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); - } - return allChildrenEqual; -} - -void* QRgbAttribute::mix(void* first, void* second, float alpha) const { - QRgb firstValue = decodeInline(first); - QRgb secondValue = decodeInline(second); - return encodeInline(qRgba( - glm::mix((float)qRed(firstValue), (float)qRed(secondValue), alpha), - glm::mix((float)qGreen(firstValue), (float)qGreen(secondValue), alpha), - glm::mix((float)qBlue(firstValue), (float)qBlue(secondValue), alpha), - glm::mix((float)qAlpha(firstValue), (float)qAlpha(secondValue), alpha))); -} - -const float EIGHT_BIT_MAXIMUM = 255.0f; - -void* QRgbAttribute::blend(void* source, void* dest) const { - QRgb sourceValue = decodeInline(source); - QRgb destValue = decodeInline(dest); - float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM; - return encodeInline(qRgba( - glm::mix((float)qRed(destValue), (float)qRed(sourceValue), alpha), - glm::mix((float)qGreen(destValue), (float)qGreen(sourceValue), alpha), - glm::mix((float)qBlue(destValue), (float)qBlue(sourceValue), alpha), - glm::mix((float)qAlpha(destValue), (float)qAlpha(sourceValue), alpha))); -} - -void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const { - return encodeInline((QRgb)value.toUInt32()); -} - -void* QRgbAttribute::createFromVariant(const QVariant& value) const { - switch (value.userType()) { - case QMetaType::QColor: - return encodeInline(value.value().rgba()); - - default: - return encodeInline((QRgb)value.toUInt()); - } -} - -QWidget* QRgbAttribute::createEditor(QWidget* parent) const { - QColorEditor* editor = new QColorEditor(parent); - editor->setColor(QColor::fromRgba(_defaultValue)); - return editor; -} - -PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultValue) : - QRgbAttribute(name, defaultValue) { -} - -bool PackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const { - QRgb firstValue = decodeInline(children[0]); - glm::vec3 total = unpackNormal(firstValue) * (float)qAlpha(firstValue); - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - total += unpackNormal(value) * (float)qAlpha(value); - allChildrenEqual &= (firstValue == value); - } - float length = glm::length(total); - parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length)); - return allChildrenEqual; -} - -void* PackedNormalAttribute::mix(void* first, void* second, float alpha) const { - glm::vec3 firstNormal = unpackNormal(decodeInline(first)); - glm::vec3 secondNormal = unpackNormal(decodeInline(second)); - return encodeInline(packNormal(glm::normalize(glm::mix(firstNormal, secondNormal, alpha)))); -} - -void* PackedNormalAttribute::blend(void* source, void* dest) const { - QRgb sourceValue = decodeInline(source); - QRgb destValue = decodeInline(dest); - float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM; - return encodeInline(packNormal(glm::normalize(glm::mix(unpackNormal(destValue), unpackNormal(sourceValue), alpha)))); +FloatAttribute::FloatAttribute(const QString& name) : + SimpleInlineAttribute(name) { } const float CHAR_SCALE = 127.0f; @@ -415,106 +301,6 @@ glm::vec3 unpackNormal(QRgb value) { (char)qBlue(value) * INVERSE_CHAR_SCALE); } -SpannerQRgbAttribute::SpannerQRgbAttribute(const QString& name, QRgb defaultValue) : - QRgbAttribute(name, defaultValue) { -} - -void SpannerQRgbAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - value = getDefaultValue(); - in.read(&value, 32); -} - -void SpannerQRgbAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - out.write(&value, 32); -} - -MetavoxelNode* SpannerQRgbAttribute::createMetavoxelNode( - const AttributeValue& value, const MetavoxelNode* original) const { - return new MetavoxelNode(value, original); -} - -bool SpannerQRgbAttribute::merge(void*& parent, void* children[], bool postRead) const { - if (postRead) { - for (int i = 0; i < MERGE_COUNT; i++) { - if (qAlpha(decodeInline(children[i])) != 0) { - return false; - } - } - return true; - } - QRgb parentValue = decodeInline(parent); - int totalAlpha = qAlpha(parentValue) * Attribute::MERGE_COUNT; - int totalRed = qRed(parentValue) * totalAlpha; - int totalGreen = qGreen(parentValue) * totalAlpha; - int totalBlue = qBlue(parentValue) * totalAlpha; - bool allChildrenTransparent = true; - for (int i = 0; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - totalRed += qRed(value) * alpha; - totalGreen += qGreen(value) * alpha; - totalBlue += qBlue(value) * alpha; - totalAlpha += alpha; - allChildrenTransparent &= (alpha == 0); - } - if (totalAlpha == 0) { - parent = encodeInline(QRgb()); - } else { - parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, - totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); - } - return allChildrenTransparent; -} - -AttributeValue SpannerQRgbAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -SpannerPackedNormalAttribute::SpannerPackedNormalAttribute(const QString& name, QRgb defaultValue) : - PackedNormalAttribute(name, defaultValue) { -} - -void SpannerPackedNormalAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - value = getDefaultValue(); - in.read(&value, 32); -} - -void SpannerPackedNormalAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - out.write(&value, 32); -} - -MetavoxelNode* SpannerPackedNormalAttribute::createMetavoxelNode( - const AttributeValue& value, const MetavoxelNode* original) const { - return new MetavoxelNode(value, original); -} - -bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const { - if (postRead) { - for (int i = 0; i < MERGE_COUNT; i++) { - if (qAlpha(decodeInline(children[i])) != 0) { - return false; - } - } - return true; - } - QRgb parentValue = decodeInline(parent); - glm::vec3 total = unpackNormal(parentValue) * (float)(qAlpha(parentValue) * Attribute::MERGE_COUNT); - bool allChildrenTransparent = true; - for (int i = 0; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - total += unpackNormal(value) * (float)alpha; - allChildrenTransparent &= (alpha == 0); - } - float length = glm::length(total); - parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length)); - return allChildrenTransparent; -} - -AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - DataBlock::~DataBlock() { } @@ -1426,6 +1212,8 @@ static QHash countIndices(const QByteArray& contents) { return counts; } +const float EIGHT_BIT_MAXIMUM = 255.0f; + uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { if (!(material && static_cast(material.data())->getDiffuse().isValid())) { return 0; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 945c4df94b..8df23bfae5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -88,21 +88,6 @@ public: /// Returns a reference to the standard SharedObjectSet "spanners" attribute. const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; } - /// Returns a reference to the standard QRgb "color" attribute. - const AttributePointer& getColorAttribute() const { return _colorAttribute; } - - /// Returns a reference to the standard packed normal "normal" attribute. - const AttributePointer& getNormalAttribute() const { return _normalAttribute; } - - /// Returns a reference to the standard QRgb "spannerColor" attribute. - const AttributePointer& getSpannerColorAttribute() const { return _spannerColorAttribute; } - - /// Returns a reference to the standard packed normal "spannerNormal" attribute. - const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; } - - /// Returns a reference to the standard "spannerMask" attribute. - const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } - /// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute. const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; } @@ -131,11 +116,6 @@ private: AttributePointer _guideAttribute; AttributePointer _rendererAttribute; AttributePointer _spannersAttribute; - AttributePointer _colorAttribute; - AttributePointer _normalAttribute; - AttributePointer _spannerColorAttribute; - AttributePointer _spannerNormalAttribute; - AttributePointer _spannerMaskAttribute; AttributePointer _heightfieldAttribute; AttributePointer _heightfieldColorAttribute; AttributePointer _heightfieldMaterialAttribute; @@ -366,51 +346,13 @@ template inline bool SimpleInlineAttribute::merge( return allChildrenEqual; } -/// Simple float attribute. +/// A simple float attribute. class FloatAttribute : public SimpleInlineAttribute { Q_OBJECT - Q_PROPERTY(float defaultValue MEMBER _defaultValue) - + public: - Q_INVOKABLE FloatAttribute(const QString& name = QString(), float defaultValue = 0.0f); -}; - -/// Provides appropriate averaging for RGBA values. -class QRgbAttribute : public InlineAttribute { - Q_OBJECT - Q_PROPERTY(uint defaultValue MEMBER _defaultValue) - -public: - - Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual void* mix(void* first, void* second, float alpha) const; - - virtual void* blend(void* source, void* dest) const; - - virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; - - virtual void* createFromVariant(const QVariant& value) const; - - virtual QWidget* createEditor(QWidget* parent = NULL) const; -}; - -/// Provides appropriate averaging for packed normals. -class PackedNormalAttribute : public QRgbAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual void* mix(void* first, void* second, float alpha) const; - - virtual void* blend(void* source, void* dest) const; + Q_INVOKABLE FloatAttribute(const QString& name = QString()); }; /// Packs a normal into an RGB value. @@ -422,42 +364,6 @@ QRgb packNormal(const glm::vec3& normal, int alpha); /// Unpacks a normal from an RGB value. glm::vec3 unpackNormal(QRgb value); -/// RGBA values for voxelized spanners. -class SpannerQRgbAttribute : public QRgbAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE SpannerQRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - -/// Packed normals for voxelized spanners. -class SpannerPackedNormalAttribute : public PackedNormalAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE SpannerPackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - typedef QExplicitlySharedDataPointer DataBlockPointer; /// Base class for blocks of data. diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index f1c086da67..353e6e8f35 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -63,19 +63,6 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } -void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(center); - sphere->setScale(radius); - sphere->setColor(color); - setSpanner(sphere); -} - -void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool reliable) { - MetavoxelEditMessage edit = { QVariant::fromValue(SetSpannerEdit(object)) }; - applyEdit(edit, reliable); -} - void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) { MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldHeightEdit(position, radius, height)) }; applyEdit(edit, true); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index adab59e0ff..bdf56400b0 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -37,10 +37,6 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); - Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, const QColor& color = QColor(Qt::gray)); - - Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false); - Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index b79903ba8a..4dcc4bd239 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -21,12 +20,9 @@ #include "MetavoxelData.h" #include "MetavoxelUtil.h" -#include "ScriptCache.h" REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) -REGISTER_META_OBJECT(ScriptedMetavoxelGuide) -REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) REGISTER_META_OBJECT(MetavoxelRenderer) REGISTER_META_OBJECT(DefaultMetavoxelRenderer) REGISTER_META_OBJECT(Spanner) @@ -401,7 +397,7 @@ private: FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) : RaySpannerIntersectionVisitor(origin, direction, QVector() << attribute, - QVector(), QVector(), QVector(), lod), + QVector(), QVector(), lod), _spanner(NULL) { } @@ -714,6 +710,7 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l return false; } if (_roots.size() != other._roots.size()) { + qDebug() << _roots << other._roots; return false; } glm::vec3 minimum = getMinimum(); @@ -1367,12 +1364,10 @@ MetavoxelVisitation& MetavoxelVisitor::acquireVisitation() { return _visitations[_depth]; } -SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& spannerMasks, - const QVector& inputs, const QVector& outputs, - const MetavoxelLOD& lod, int order) : - MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod), +SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod, int order) : + MetavoxelVisitor(inputs + spannerInputs, outputs, lod), _spannerInputCount(spannerInputs.size()), - _spannerMaskCount(spannerMasks.size()), _order(order) { } @@ -1382,34 +1377,15 @@ void SpannerVisitor::prepare(MetavoxelData* data) { } int SpannerVisitor::visit(MetavoxelInfo& info) { - for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { + for (int end = _inputs.size(), i = end - _spannerInputCount; i < end; i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited(_visit) && - !visit(spanner, glm::vec3(), 0.0f)) { + if (spanner->testAndSetVisited(_visit) && !visit(spanner)) { return SHORT_CIRCUIT; } } } - if (!info.isLeaf) { - return _order; - } - for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { - float maskValue = info.inputValues.at(i).getInlineValue(); - if (maskValue < 0.5f) { - const MetavoxelInfo* nextInfo = &info; - do { - foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( - i - _spannerInputCount).getInlineValue()) { - Spanner* spanner = static_cast(object.data()); - if (spanner->isMasked() && !visit(spanner, info.minimum, info.size)) { - return SHORT_CIRCUIT; - } - } - } while ((nextInfo = nextInfo->parentInfo)); - } - } - return STOP_RECURSION; + return info.isLeaf ? STOP_RECURSION : _order; } RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, @@ -1429,11 +1405,10 @@ int RayIntersectionVisitor::visit(MetavoxelInfo& info) { } RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& spannerInputs, const QVector& spannerMasks, - const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, inputs + spannerInputs + spannerMasks, outputs, lod), - _spannerInputCount(spannerInputs.size()), - _spannerMaskCount(spannerMasks.size()) { + const QVector& spannerInputs, const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), + _spannerInputCount(spannerInputs.size()) { } void RaySpannerIntersectionVisitor::prepare(MetavoxelData* data) { @@ -1453,12 +1428,12 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; - for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { + for (int end = _inputs.size(), i = end - _spannerInputCount; i < end; i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited(_visit)) { + if (spanner->testAndSetVisited(_visit)) { SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) { + if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) { spannerDistances.append(spannerDistance); } } @@ -1470,36 +1445,7 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { } } } - if (!info.isLeaf) { - return _order; - } - for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { - float maskValue = info.inputValues.at(i).getInlineValue(); - if (maskValue < 0.5f) { - const MetavoxelInfo* nextInfo = &info; - do { - foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( - i - _spannerInputCount).getInlineValue()) { - Spanner* spanner = static_cast(object.data()); - if (spanner->isMasked()) { - SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, - info.minimum, info.size, spannerDistance.distance)) { - spannerDistances.append(spannerDistance); - } - } - } - } while ((nextInfo = nextInfo->parentInfo)); - - qStableSort(spannerDistances); - foreach (const SpannerDistance& spannerDistance, spannerDistances) { - if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { - return SHORT_CIRCUIT; - } - } - } - } - return STOP_RECURSION; + return info.isLeaf ? STOP_RECURSION : _order; } bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { @@ -1765,143 +1711,6 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { return true; } -ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(10.0) { -} - -bool ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute(); - for (int i = 0; i < visitation.info.inputValues.size(); i++) { - AttributeValue& attributeValue = visitation.info.inputValues[i]; - if (attributeValue.getAttribute() == colorAttribute) { - QRgb base = attributeValue.getInlineValue(); - double seconds = QDateTime::currentMSecsSinceEpoch() / 1000.0; - double amplitude = sin(_rate * seconds) * 0.5 + 0.5; - attributeValue.setInlineValue(qRgba(qRed(base) * amplitude, qGreen(base) * amplitude, - qBlue(base) * amplitude, qAlpha(base))); - } - } - - return DefaultMetavoxelGuide::guide(visitation); -} - -static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, - const QVector& attributes) { - - QScriptValue attributesValue = engine->newArray(attributes.size()); - for (int i = 0; i < attributes.size(); i++) { - attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership, - QScriptEngine::PreferExistingWrapperObject)); - } - return attributesValue; -} - -QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); - return getAttributes(engine, guide, guide->_visitation->visitor->getInputs()); -} - -QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); - return getAttributes(engine, guide, guide->_visitation->visitor->getOutputs()); -} - -QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); - - // start with the basics, including inherited attribute values - QScriptValue infoValue = context->argument(0); - QScriptValue minimum = infoValue.property(guide->_minimumHandle); - MetavoxelInfo info(NULL, 0, 0); - info.inputValues = guide->_visitation->info.inputValues; - info.outputValues = guide->_visitation->info.outputValues; - info.minimum = glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()); - info.size = (float)infoValue.property(guide->_sizeHandle).toNumber(); - info.isLeaf = infoValue.property(guide->_isLeafHandle).toBool(); - - // extract and convert the values provided by the script - QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle); - const QVector& inputs = guide->_visitation->visitor->getInputs(); - for (int i = 0; i < inputs.size(); i++) { - QScriptValue attributeValue = inputValues.property(i); - if (attributeValue.isValid()) { - info.inputValues[i] = AttributeValue(inputs.at(i), - inputs.at(i)->createFromScript(attributeValue, engine)); - } - } - - QScriptValue result = guide->_visitation->visitor->visit(info); - - // destroy any created values - for (int i = 0; i < inputs.size(); i++) { - if (inputValues.property(i).isValid()) { - info.inputValues[i].getAttribute()->destroy(info.inputValues[i].getValue()); - } - } - - return result; -} - -ScriptedMetavoxelGuide::ScriptedMetavoxelGuide() { -} - -bool ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - QScriptValue guideFunction; - if (_guideFunction) { - guideFunction = _guideFunction->getValue(); - - } else if (_url.isValid()) { - _guideFunction = ScriptCache::getInstance()->getValue(_url); - guideFunction = _guideFunction->getValue(); - } - if (!guideFunction.isValid()) { - // before we load, just use the default behavior - return DefaultMetavoxelGuide::guide(visitation); - } - QScriptEngine* engine = guideFunction.engine(); - if (!_minimumHandle.isValid()) { - _minimumHandle = engine->toStringHandle("minimum"); - _sizeHandle = engine->toStringHandle("size"); - _inputValuesHandle = engine->toStringHandle("inputValues"); - _outputValuesHandle = engine->toStringHandle("outputValues"); - _isLeafHandle = engine->toStringHandle("isLeaf"); - _getInputsFunction = engine->newFunction(getInputs, 0); - _getOutputsFunction = engine->newFunction(getOutputs, 0); - _visitFunction = engine->newFunction(visit, 1); - _info = engine->newObject(); - _minimum = engine->newArray(3); - - _arguments.clear(); - _arguments.append(engine->newObject()); - QScriptValue visitor = engine->newObject(); - visitor.setProperty("getInputs", _getInputsFunction); - visitor.setProperty("getOutputs", _getOutputsFunction); - visitor.setProperty("visit", _visitFunction); - _arguments[0].setProperty("visitor", visitor); - _arguments[0].setProperty("info", _info); - _info.setProperty(_minimumHandle, _minimum); - } - QScriptValue data = engine->newVariant(QVariant::fromValue(this)); - _getInputsFunction.setData(data); - _visitFunction.setData(data); - _minimum.setProperty(0, visitation.info.minimum.x); - _minimum.setProperty(1, visitation.info.minimum.y); - _minimum.setProperty(2, visitation.info.minimum.z); - _info.setProperty(_sizeHandle, visitation.info.size); - _info.setProperty(_isLeafHandle, visitation.info.isLeaf); - _visitation = &visitation; - guideFunction.call(QScriptValue(), _arguments); - if (engine->hasUncaughtException()) { - qDebug() << "Script error: " << engine->uncaughtException().toString(); - } - return true; -} - -void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) { - _url = url; - _guideFunction.reset(); - _minimumHandle = QScriptString(); -} - MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize) : previous(previous), @@ -1997,8 +1806,7 @@ const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); Spanner::Spanner() : _renderer(NULL), _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), - _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), - _masked(false) { + _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY) { } void Spanner::setBounds(const Box& bounds) { @@ -2009,24 +1817,6 @@ void Spanner::setBounds(const Box& bounds) { emit boundsChanged(_bounds = bounds); } -const QVector& Spanner::getAttributes() const { - static QVector emptyVector; - return emptyVector; -} - -const QVector& Spanner::getVoxelizedAttributes() const { - static QVector emptyVector; - return emptyVector; -} - -bool Spanner::getAttributeValues(MetavoxelInfo& info, bool force) const { - return false; -} - -bool Spanner::blendAttributeValues(MetavoxelInfo& info, bool force) const { - return false; -} - bool Spanner::testAndSetVisited(int visit) { QMutexLocker locker(&_lastVisitsMutex); int& lastVisit = _lastVisits[QThread::currentThread()]; @@ -2052,8 +1842,7 @@ SpannerRenderer* Spanner::getRenderer() { return _renderer; } -bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { +bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { return _bounds.findRayIntersection(origin, direction, distance); } @@ -2103,12 +1892,11 @@ void SpannerRenderer::simulate(float deltaTime) { // nothing by default } -void SpannerRenderer::render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize) { +void SpannerRenderer::render(const glm::vec4& color, Mode mode) { // nothing by default } -bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { +bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { return false; } @@ -2149,92 +1937,7 @@ Sphere::Sphere() { updateBounds(); } -const QVector& Sphere::getAttributes() const { - static QVector attributes = QVector() << - AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute(); - return attributes; -} - -const QVector& Sphere::getVoxelizedAttributes() const { - static QVector attributes = QVector() << - AttributeRegistry::getInstance()->getSpannerColorAttribute() << - AttributeRegistry::getInstance()->getSpannerNormalAttribute(); - return attributes; -} - -bool Sphere::getAttributeValues(MetavoxelInfo& info, bool force) const { - // bounds check - Box bounds = info.getBounds(); - if (!(force || getBounds().intersects(bounds))) { - return false; - } - // count the points inside the sphere - int pointsWithin = 0; - for (int i = 0; i < Box::VERTEX_COUNT; i++) { - if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { - pointsWithin++; - } - } - if (pointsWithin == Box::VERTEX_COUNT) { - // entirely contained - info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); - info.outputValues[1] = getNormal(info, _color.alpha()); - return false; - } - if (force || info.size <= getVoxelizationGranularity()) { - // best guess - if (pointsWithin > 0) { - int alpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; - info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(qRgba( - _color.red(), _color.green(), _color.blue(), alpha))); - info.outputValues[1] = getNormal(info, alpha); - } - return false; - } - return true; -} - -bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const { - // bounds check - Box bounds = info.getBounds(); - if (!(force || getBounds().intersects(bounds))) { - return false; - } - // count the points inside the sphere - int pointsWithin = 0; - for (int i = 0; i < Box::VERTEX_COUNT; i++) { - if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { - pointsWithin++; - } - } - if (pointsWithin == Box::VERTEX_COUNT) { - // entirely contained - info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); - info.outputValues[1] = getNormal(info, _color.alpha()); - return false; - } - if (force || info.size <= getVoxelizationGranularity()) { - // best guess - if (pointsWithin > 0) { - const AttributeValue& oldColor = info.outputValues.at(0).getAttribute() ? - info.outputValues.at(0) : info.inputValues.at(0); - const AttributeValue& oldNormal = info.outputValues.at(1).getAttribute() ? - info.outputValues.at(1) : info.inputValues.at(1); - int oldAlpha = qAlpha(oldColor.getInlineValue()); - int newAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; - float combinedAlpha = (float)newAlpha / (oldAlpha + newAlpha); - int baseAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; - info.outputValues[0].mix(oldColor, AttributeValue(getAttributes().at(0), - encodeInline(qRgba(_color.red(), _color.green(), _color.blue(), baseAlpha))), combinedAlpha); - info.outputValues[1].mix(oldNormal, getNormal(info, baseAlpha), combinedAlpha); - } - return false; - } - return true; -} - -bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { +bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); } @@ -2279,23 +1982,6 @@ void Sphere::updateBounds() { setBounds(Box(getTranslation() - extent, getTranslation() + extent)); } -AttributeValue Sphere::getNormal(MetavoxelInfo& info, int alpha) const { - glm::vec3 normal = info.getCenter() - getTranslation(); - float length = glm::length(normal); - QRgb color; - if (alpha != 0 && length > EPSILON) { - const float NORMAL_SCALE = 127.0f; - float scale = NORMAL_SCALE / length; - const int BYTE_MASK = 0xFF; - color = qRgba((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK, - (int)(normal.z * scale) & BYTE_MASK, alpha); - - } else { - color = QRgb(); - } - return AttributeValue(getAttributes().at(1), encodeInline(color)); -} - Cuboid::Cuboid() : _aspectY(1.0f), _aspectZ(1.0f) { @@ -2388,11 +2074,10 @@ void StaticModel::setURL(const QUrl& url) { } } -bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { +bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { // delegate to renderer, if we have one - return _renderer ? _renderer->findRayIntersection(origin, direction, clipMinimum, clipSize, distance) : - Spanner::findRayIntersection(origin, direction, clipMinimum, clipSize, distance); + return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : + Spanner::findRayIntersection(origin, direction, distance); } QByteArray StaticModel::getRendererClassName() const { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 3efb0fc8f2..5dc1a8e7c2 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -12,13 +12,10 @@ #ifndef hifi_MetavoxelData_h #define hifi_MetavoxelData_h -#include #include #include #include #include -#include -#include #include #include @@ -26,8 +23,6 @@ #include "AttributeRegistry.h" #include "MetavoxelUtil.h" -class QScriptContext; - class MetavoxelInfo; class MetavoxelNode; class MetavoxelRendererImplementation; @@ -373,16 +368,14 @@ class SpannerVisitor : public MetavoxelVisitor { public: SpannerVisitor(const QVector& spannerInputs, - const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD(), int order = DEFAULT_ORDER); - /// Visits a spanner (or part thereof). - /// \param clipSize the size of the clip volume, or zero if unclipped + /// Visits a spanner. /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0; + virtual bool visit(Spanner* spanner) = 0; virtual void prepare(MetavoxelData* data); virtual int visit(MetavoxelInfo& info); @@ -390,7 +383,6 @@ public: protected: int _spannerInputCount; - int _spannerMaskCount; int _order; int _visit; }; @@ -422,12 +414,11 @@ public: RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const QVector& spannerInputs, - const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD()); - /// Visits a spannerthat the ray intersects. + /// Visits a spanner that the ray intersects. /// \return true to continue, false to short-circuit the tour virtual bool visitSpanner(Spanner* spanner, float distance) = 0; @@ -437,7 +428,6 @@ public: protected: int _spannerInputCount; - int _spannerMaskCount; int _visit; }; @@ -468,62 +458,6 @@ public: virtual bool guideToDifferent(MetavoxelVisitation& visitation); }; -/// A temporary test guide that just makes the existing voxels throb with delight. -class ThrobbingMetavoxelGuide : public DefaultMetavoxelGuide { - Q_OBJECT - Q_PROPERTY(float rate MEMBER _rate) - -public: - - Q_INVOKABLE ThrobbingMetavoxelGuide(); - - virtual bool guide(MetavoxelVisitation& visitation); - -private: - - float _rate; -}; - -/// Represents a guide implemented in Javascript. -class ScriptedMetavoxelGuide : public DefaultMetavoxelGuide { - Q_OBJECT - Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL) - -public: - - Q_INVOKABLE ScriptedMetavoxelGuide(); - - virtual bool guide(MetavoxelVisitation& visitation); - -public slots: - - void setURL(const ParameterizedURL& url); - -private: - - static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine); - static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine); - static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); - - ParameterizedURL _url; - - QSharedPointer _guideFunction; - - QScriptString _minimumHandle; - QScriptString _sizeHandle; - QScriptString _inputValuesHandle; - QScriptString _outputValuesHandle; - QScriptString _isLeafHandle; - QScriptValueList _arguments; - QScriptValue _getInputsFunction; - QScriptValue _getOutputsFunction; - QScriptValue _visitFunction; - QScriptValue _info; - QScriptValue _minimum; - - MetavoxelVisitation* _visitation; -}; - /// Contains the state associated with a visit to a metavoxel system. class MetavoxelVisitation { public: @@ -598,7 +532,6 @@ class Spanner : public SharedObject { Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) - Q_PROPERTY(float masked MEMBER _masked DESIGNABLE false) public: @@ -616,24 +549,6 @@ public: void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } float getVoxelizationGranularity() const { return _voxelizationGranularity; } - void setMasked(bool masked) { _masked = masked; } - bool isMasked() const { return _masked; } - - /// Returns a reference to the list of attributes associated with this spanner. - virtual const QVector& getAttributes() const; - - /// Returns a reference to the list of corresponding attributes that we voxelize the spanner into. - virtual const QVector& getVoxelizedAttributes() const; - - /// Sets the attribute values associated with this spanner in the supplied info. - /// \return true to recurse, false to stop - virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; - - /// Blends the attribute values associated with this spanner into the supplied info. - /// \param force if true, blend even if we would normally subdivide - /// \return true to recurse, false to stop - virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; - /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(int visit); @@ -642,9 +557,7 @@ public: SpannerRenderer* getRenderer(); /// Finds the intersection between the described ray and this spanner. - /// \param clipSize the size of the clip region, or zero if unclipped - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -684,7 +597,6 @@ private: Box _bounds; float _placementGranularity; float _voxelizationGranularity; - bool _masked; QHash _lastVisits; ///< last visit identifiers for each thread QMutex _lastVisitsMutex; @@ -703,9 +615,8 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; + virtual void render(const glm::vec4& color, Mode mode); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; protected: @@ -774,12 +685,7 @@ public: Q_INVOKABLE Sphere(); - virtual const QVector& getAttributes() const; - virtual const QVector& getVoxelizedAttributes() const; - virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; - virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; virtual bool contains(const glm::vec3& point); virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); @@ -790,10 +696,6 @@ protected: private slots: void updateBounds(); - -private: - - AttributeValue getNormal(MetavoxelInfo& info, int alpha) const; }; /// A cuboid. @@ -849,8 +751,7 @@ public: void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; signals: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index c9c2d32b7e..64e7863dee 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -39,8 +39,7 @@ private: }; BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : - MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute() << - AttributeRegistry::getInstance()->getSpannerMaskAttribute()), + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), _edit(edit) { } @@ -55,57 +54,17 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) { float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); if (volume >= 1.0f) { info.outputValues[0] = _edit.value; - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); return STOP_RECURSION; // entirely contained } if (info.size <= _edit.granularity) { if (volume >= 0.5f) { info.outputValues[0] = _edit.value; - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); } return STOP_RECURSION; // reached granularity limit; take best guess } return DEFAULT_ORDER; // subdivide } -class GatherUnmaskedSpannersVisitor : public SpannerVisitor { -public: - - GatherUnmaskedSpannersVisitor(const Box& bounds); - - const QList& getUnmaskedSpanners() const { return _unmaskedSpanners; } - - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); - -private: - - Box _bounds; - QList _unmaskedSpanners; -}; - -GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), - _bounds(bounds) { -} - -bool GatherUnmaskedSpannersVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - if (!spanner->isMasked() && spanner->getBounds().intersects(_bounds)) { - _unmaskedSpanners.append(spanner); - } - return true; -} - -static void setIntersectingMasked(const Box& bounds, MetavoxelData& data) { - GatherUnmaskedSpannersVisitor visitor(bounds); - data.guide(visitor); - - foreach (const SharedObjectPointer& object, visitor.getUnmaskedSpanners()) { - Spanner* newSpanner = static_cast(object->clone(true)); - newSpanner->setMasked(true); - data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), object, newSpanner); - } -} - void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { @@ -114,9 +73,6 @@ void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) BoxSetEditVisitor setVisitor(*this); data.guide(setVisitor); - - // flip the mask flag of all intersecting spanners - setIntersectingMasked(region, data); } GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) : @@ -155,56 +111,8 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } -class UpdateSpannerVisitor : public MetavoxelVisitor { -public: - - UpdateSpannerVisitor(const QVector& attributes, Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - Spanner* _spanner; - float _voxelizationSize; - int _steps; -}; - -UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(QVector() << attributes << AttributeRegistry::getInstance()->getSpannersAttribute(), - attributes), - _spanner(spanner), - _voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f / - AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()), - _steps(glm::round(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) / - logf(2.0f) - 2.0f)) { -} - -int UpdateSpannerVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_spanner->getBounds())) { - return STOP_RECURSION; - } - MetavoxelInfo* parentInfo = info.parentInfo; - for (int i = 0; i < _steps && parentInfo; i++) { - parentInfo = parentInfo->parentInfo; - } - for (int i = 0; i < _outputs.size(); i++) { - info.outputValues[i] = AttributeValue(_outputs.at(i)); - } - if (parentInfo) { - foreach (const SharedObjectPointer& object, - parentInfo->inputValues.at(_outputs.size()).getInlineValue()) { - static_cast(object.data())->blendAttributeValues(info, true); - } - } - return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; -} - void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.insert(attribute, this->spanner); - - Spanner* spanner = static_cast(this->spanner.data()); - UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner); - data.guide(visitor); + data.insert(attribute, spanner); } RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : @@ -218,99 +126,15 @@ void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o qDebug() << "Missing object to remove" << id; return; } - // keep a strong reference to the object - SharedObjectPointer sharedPointer = object; data.remove(attribute, object); - - Spanner* spanner = static_cast(object); - UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner); - data.guide(visitor); } ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : attribute(attribute) { } -class GatherSpannerAttributesVisitor : public SpannerVisitor { -public: - - GatherSpannerAttributesVisitor(const AttributePointer& attribute); - - const QSet& getAttributes() const { return _attributes; } - - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); - -protected: - - QSet _attributes; -}; - -GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) : - SpannerVisitor(QVector() << attribute) { -} - -bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) { - _attributes.insert(attribute); - } - return true; -} - void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // find all the spanner attributes - GatherSpannerAttributesVisitor visitor(attribute); - data.guide(visitor); - data.clear(attribute); - foreach (const AttributePointer& attribute, visitor.getAttributes()) { - data.clear(attribute); - } -} - -SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : - spanner(spanner) { -} - -class SetSpannerEditVisitor : public MetavoxelVisitor { -public: - - SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - Spanner* _spanner; -}; - -SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(attributes, QVector() << attributes << - AttributeRegistry::getInstance()->getSpannerMaskAttribute()), - _spanner(spanner) { -} - -int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { - if (_spanner->blendAttributeValues(info)) { - return DEFAULT_ORDER; - } - if (info.outputValues.at(0).getAttribute()) { - info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline(1.0f)); - } - return STOP_RECURSION; -} - -void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - Spanner* spanner = static_cast(this->spanner.data()); - - // expand to fit the entire spanner - while (!data.getBounds().contains(spanner->getBounds())) { - data.expand(); - } - - SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner); - data.guide(visitor); - - setIntersectingMasked(spanner->getBounds(), data); } SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) : diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 06f2fc9b8d..10477e0486 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -177,21 +177,6 @@ public: DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) -/// An edit that sets a spanner's attributes in the voxel tree. -class SetSpannerEdit : public MetavoxelEdit { - STREAMABLE - -public: - - STREAM SharedObjectPointer spanner; - - SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer()); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(SetSpannerEdit) - /// An edit that directly sets part of the metavoxel data. class SetDataEdit : public MetavoxelEdit { STREAMABLE diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 92f8d0568c..5a6355483d 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -439,12 +439,17 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { return false; } +static AttributePointer simpleAttribute; + bool MetavoxelTests::run() { LimitedNodeList::createInstance(); // seed the random number generator so that our tests are reproducible srand(0xBAAAAABE); + // register our test attribute + simpleAttribute = AttributeRegistry::getInstance()->registerAttribute(new FloatAttribute("simpleAttribute")); + // check for an optional command line argument specifying a single test QStringList arguments = this->arguments(); int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0; @@ -582,8 +587,7 @@ public: }; RandomVisitor::RandomVisitor() : - MetavoxelVisitor(QVector(), - QVector() << AttributeRegistry::getInstance()->getColorAttribute()), + MetavoxelVisitor(QVector(), QVector() << simpleAttribute), leafCount(0) { } @@ -594,8 +598,7 @@ int RandomVisitor::visit(MetavoxelInfo& info) { if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { return DEFAULT_ORDER; } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randomColorValue(), - randomColorValue(), randomColorValue()))); + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(randFloat())); leafCount++; return STOP_RECURSION; } @@ -812,8 +815,7 @@ private: }; MutateVisitor::MutateVisitor() : - MetavoxelVisitor(QVector(), - QVector() << AttributeRegistry::getInstance()->getColorAttribute()), + MetavoxelVisitor(QVector(), QVector() << simpleAttribute), _mutationsRemaining(randIntInRange(2, 4)) { } @@ -824,8 +826,7 @@ int MutateVisitor::visit(MetavoxelInfo& info) { if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { return encodeRandomOrder(); } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randomColorValue(), - randomColorValue(), randomColorValue()))); + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(randFloat())); _mutationsRemaining--; metavoxelMutationsPerformed++; return STOP_RECURSION; From 4c60be4a31c8395e64ba1f838732cb8b780a1b9b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Nov 2014 19:37:37 -0800 Subject: [PATCH 02/38] Fix for hang on test exit. --- tests/metavoxels/src/MetavoxelTests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 5a6355483d..0609d7bd01 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -439,8 +439,6 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { return false; } -static AttributePointer simpleAttribute; - bool MetavoxelTests::run() { LimitedNodeList::createInstance(); @@ -448,7 +446,7 @@ bool MetavoxelTests::run() { srand(0xBAAAAABE); // register our test attribute - simpleAttribute = AttributeRegistry::getInstance()->registerAttribute(new FloatAttribute("simpleAttribute")); + AttributePointer testAttribute = AttributeRegistry::getInstance()->registerAttribute(new FloatAttribute("testAttribute")); // check for an optional command line argument specifying a single test QStringList arguments = this->arguments(); @@ -587,7 +585,8 @@ public: }; RandomVisitor::RandomVisitor() : - MetavoxelVisitor(QVector(), QVector() << simpleAttribute), + MetavoxelVisitor(QVector(), QVector() << + AttributeRegistry::getInstance()->getAttribute("testAttribute")), leafCount(0) { } @@ -815,7 +814,8 @@ private: }; MutateVisitor::MutateVisitor() : - MetavoxelVisitor(QVector(), QVector() << simpleAttribute), + MetavoxelVisitor(QVector(), QVector() << + AttributeRegistry::getInstance()->getAttribute("testAttribute")), _mutationsRemaining(randIntInRange(2, 4)) { } From 536d6287bdbadd6b7b9181da2e901966eaedb1e7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Nov 2014 11:44:42 -0800 Subject: [PATCH 03/38] Added merged flag to Spanner. --- libraries/metavoxels/src/MetavoxelData.cpp | 3 ++- libraries/metavoxels/src/MetavoxelData.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 4dcc4bd239..de9d4c104b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1806,7 +1806,8 @@ const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); Spanner::Spanner() : _renderer(NULL), _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), - _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY) { + _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), + _merged(false) { } void Spanner::setBounds(const Box& bounds) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 5dc1a8e7c2..5033d116b0 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -549,6 +549,9 @@ public: void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } float getVoxelizationGranularity() const { return _voxelizationGranularity; } + void setMerged(bool merged) { _merged = merged; } + bool isMerged() const { return _merged; } + /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(int visit); @@ -597,6 +600,7 @@ private: Box _bounds; float _placementGranularity; float _voxelizationGranularity; + bool _merged; QHash _lastVisits; ///< last visit identifiers for each thread QMutex _lastVisitsMutex; From 0664ff3181378a820283e93ac923c6949b16f6f5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Nov 2014 15:51:41 -0800 Subject: [PATCH 04/38] Moved spanners into separate file, working on heightfields as spanners. --- interface/src/MetavoxelSystem.cpp | 8 + interface/src/MetavoxelSystem.h | 11 + .../metavoxels/src/AttributeRegistry.cpp | 2 + libraries/metavoxels/src/Endpoint.h | 1 + libraries/metavoxels/src/MetavoxelData.cpp | 557 +------------ libraries/metavoxels/src/MetavoxelData.h | 279 ------- .../metavoxels/src/MetavoxelMessages.cpp | 5 +- libraries/metavoxels/src/Spanner.cpp | 783 ++++++++++++++++++ libraries/metavoxels/src/Spanner.h | 454 ++++++++++ libraries/networking/src/PacketHeaders.cpp | 2 +- 10 files changed, 1264 insertions(+), 838 deletions(-) create mode 100644 libraries/metavoxels/src/Spanner.cpp create mode 100644 libraries/metavoxels/src/Spanner.h diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a671452143..d2b3e20122 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -36,6 +36,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(CuboidRenderer) REGISTER_META_OBJECT(StaticModelRenderer) +REGISTER_META_OBJECT(HeightfieldRenderer) MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, int minimumDelay, int maximumDelay, int bandwidthLimit) : @@ -2854,6 +2855,7 @@ ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram; SphereRenderer::SphereRenderer() { } + void SphereRenderer::render(const glm::vec4& color, Mode mode) { Sphere* sphere = static_cast(_spanner); const QColor& ownColor = sphere->getColor(); @@ -2969,3 +2971,9 @@ void StaticModelRenderer::applyScale(float scale) { void StaticModelRenderer::applyURL(const QUrl& url) { _model->setURL(url); } + +HeightfieldRenderer::HeightfieldRenderer() { +} + +void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { +} diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index e0fce70c2d..12d5b5bca8 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -450,4 +450,15 @@ private: Model* _model; }; +/// Renders heightfields. +class HeightfieldRenderer : public SpannerRenderer { + Q_OBJECT + +public: + + Q_INVOKABLE HeightfieldRenderer(); + + virtual void render(const glm::vec4& color, Mode mode); +}; + #endif // hifi_MetavoxelSystem_h diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 0ed20386c6..ddc23980b5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -17,6 +17,7 @@ #include "AttributeRegistry.h" #include "MetavoxelData.h" +#include "Spanner.h" REGISTER_META_OBJECT(FloatAttribute) REGISTER_META_OBJECT(MaterialObject) @@ -54,6 +55,7 @@ AttributeRegistry::AttributeRegistry() : // 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); + _spannersAttribute->setUserFacing(true); const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index 816a1fe2a9..c64f29878d 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -16,6 +16,7 @@ #include "DatagramSequencer.h" #include "MetavoxelData.h" +#include "Spanner.h" class PacketRecord; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index de9d4c104b..cb3c5b94bf 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -11,24 +11,15 @@ #include #include -#include #include -#include - -#include - #include "MetavoxelData.h" -#include "MetavoxelUtil.h" +#include "Spanner.h" REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(MetavoxelRenderer) REGISTER_META_OBJECT(DefaultMetavoxelRenderer) -REGISTER_META_OBJECT(Spanner) -REGISTER_META_OBJECT(Sphere) -REGISTER_META_OBJECT(Cuboid) -REGISTER_META_OBJECT(StaticModel) static int metavoxelDataTypeId = registerSimpleMetaType(); @@ -1800,549 +1791,3 @@ QByteArray DefaultMetavoxelRenderer::getImplementationClassName() const { return "DefaultMetavoxelRendererImplementation"; } -const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; -const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); - -Spanner::Spanner() : - _renderer(NULL), - _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), - _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), - _merged(false) { -} - -void Spanner::setBounds(const Box& bounds) { - if (_bounds == bounds) { - return; - } - emit boundsWillChange(); - emit boundsChanged(_bounds = bounds); -} - -bool Spanner::testAndSetVisited(int visit) { - QMutexLocker locker(&_lastVisitsMutex); - int& lastVisit = _lastVisits[QThread::currentThread()]; - if (lastVisit == visit) { - return false; - } - lastVisit = visit; - return true; -} - -SpannerRenderer* Spanner::getRenderer() { - if (!_renderer) { - QByteArray className = getRendererClassName(); - const QMetaObject* metaObject = Bitstream::getMetaObject(className); - if (!metaObject) { - qDebug() << "Unknown class name:" << className; - metaObject = &SpannerRenderer::staticMetaObject; - } - _renderer = static_cast(metaObject->newInstance()); - connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater); - _renderer->init(this); - } - return _renderer; -} - -bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return _bounds.findRayIntersection(origin, direction, distance); -} - -bool Spanner::hasOwnColors() const { - return false; -} - -bool Spanner::hasOwnMaterials() const { - return false; -} - -QRgb Spanner::getColorAt(const glm::vec3& point) { - return 0; -} - -int Spanner::getMaterialAt(const glm::vec3& point) { - return 0; -} - -QVector& Spanner::getMaterials() { - static QVector emptyMaterials; - return emptyMaterials; -} - -bool Spanner::contains(const glm::vec3& point) { - return false; -} - -bool Spanner::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - return false; -} - -QByteArray Spanner::getRendererClassName() const { - return "SpannerRendererer"; -} - -QAtomicInt Spanner::_nextVisit(1); - -SpannerRenderer::SpannerRenderer() { -} - -void SpannerRenderer::init(Spanner* spanner) { - _spanner = spanner; -} - -void SpannerRenderer::simulate(float deltaTime) { - // nothing by default -} - -void SpannerRenderer::render(const glm::vec4& color, Mode mode) { - // nothing by default -} - -bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return false; -} - -Transformable::Transformable() : _scale(1.0f) { -} - -void Transformable::setTranslation(const glm::vec3& translation) { - if (_translation != translation) { - emit translationChanged(_translation = translation); - } -} - -void Transformable::setRotation(const glm::quat& rotation) { - if (_rotation != rotation) { - emit rotationChanged(_rotation = rotation); - } -} - -void Transformable::setScale(float scale) { - if (_scale != scale) { - emit scaleChanged(_scale = scale); - } -} - -ColorTransformable::ColorTransformable() : - _color(Qt::white) { -} - -void ColorTransformable::setColor(const QColor& color) { - if (_color != color) { - emit colorChanged(_color = color); - } -} - -Sphere::Sphere() { - connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds())); - connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds())); - updateBounds(); -} - -bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); -} - -bool Sphere::contains(const glm::vec3& point) { - return glm::distance(point, getTranslation()) <= getScale(); -} - -bool Sphere::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - glm::vec3 relativeStart = start - getTranslation(); - glm::vec3 vector = end - start; - float a = glm::dot(vector, vector); - if (a == 0.0f) { - return false; - } - float b = glm::dot(relativeStart, vector); - float radicand = b * b - a * (glm::dot(relativeStart, relativeStart) - getScale() * getScale()); - if (radicand < 0.0f) { - return false; - } - float radical = glm::sqrt(radicand); - float first = (-b - radical) / a; - if (first >= 0.0f && first <= 1.0f) { - distance = first; - normal = glm::normalize(relativeStart + vector * distance); - return true; - } - float second = (-b + radical) / a; - if (second >= 0.0f && second <= 1.0f) { - distance = second; - normal = glm::normalize(relativeStart + vector * distance); - return true; - } - return false; -} - -QByteArray Sphere::getRendererClassName() const { - return "SphereRenderer"; -} - -void Sphere::updateBounds() { - glm::vec3 extent(getScale(), getScale(), getScale()); - setBounds(Box(getTranslation() - extent, getTranslation() + extent)); -} - -Cuboid::Cuboid() : - _aspectY(1.0f), - _aspectZ(1.0f) { - - connect(this, &Cuboid::translationChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::rotationChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::scaleChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::aspectYChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::aspectZChanged, this, &Cuboid::updateBoundsAndPlanes); - updateBoundsAndPlanes(); -} - -void Cuboid::setAspectY(float aspectY) { - if (_aspectY != aspectY) { - emit aspectYChanged(_aspectY = aspectY); - } -} - -void Cuboid::setAspectZ(float aspectZ) { - if (_aspectZ != aspectZ) { - emit aspectZChanged(_aspectZ = aspectZ); - } -} - -bool Cuboid::contains(const glm::vec3& point) { - glm::vec4 point4(point, 1.0f); - for (int i = 0; i < PLANE_COUNT; i++) { - if (glm::dot(_planes[i], point4) > 0.0f) { - return false; - } - } - return true; -} - -bool Cuboid::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - glm::vec4 start4(start, 1.0f); - glm::vec4 vector = glm::vec4(end - start, 0.0f); - for (int i = 0; i < PLANE_COUNT; i++) { - // first check the segment against the plane - float divisor = glm::dot(_planes[i], vector); - if (glm::abs(divisor) < EPSILON) { - continue; - } - float t = -glm::dot(_planes[i], start4) / divisor; - if (t < 0.0f || t > 1.0f) { - continue; - } - // now that we've established that it intersects the plane, check against the other sides - glm::vec4 point = start4 + vector * t; - const int PLANES_PER_AXIS = 2; - int indexOffset = ((i / PLANES_PER_AXIS) + 1) * PLANES_PER_AXIS; - for (int j = 0; j < PLANE_COUNT - PLANES_PER_AXIS; j++) { - if (glm::dot(_planes[(indexOffset + j) % PLANE_COUNT], point) > 0.0f) { - goto outerContinue; - } - } - distance = t; - normal = glm::vec3(_planes[i]); - return true; - - outerContinue: ; - } - return false; -} - -QByteArray Cuboid::getRendererClassName() const { - return "CuboidRenderer"; -} - -void Cuboid::updateBoundsAndPlanes() { - glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ); - glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); - setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent)); - - glm::vec4 translation4 = glm::vec4(getTranslation(), 1.0f); - _planes[0] = glm::vec4(glm::vec3(rotationMatrix[0]), -glm::dot(rotationMatrix[0], translation4) - getScale()); - _planes[1] = glm::vec4(glm::vec3(-rotationMatrix[0]), glm::dot(rotationMatrix[0], translation4) - getScale()); - _planes[2] = glm::vec4(glm::vec3(rotationMatrix[1]), -glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY); - _planes[3] = glm::vec4(glm::vec3(-rotationMatrix[1]), glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY); - _planes[4] = glm::vec4(glm::vec3(rotationMatrix[2]), -glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ); - _planes[5] = glm::vec4(glm::vec3(-rotationMatrix[2]), glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ); -} - -StaticModel::StaticModel() { -} - -void StaticModel::setURL(const QUrl& url) { - if (_url != url) { - emit urlChanged(_url = url); - } -} - -bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - // delegate to renderer, if we have one - return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : - Spanner::findRayIntersection(origin, direction, distance); -} - -QByteArray StaticModel::getRendererClassName() const { - return "StaticModelRenderer"; -} - -const float EIGHT_BIT_MAXIMUM = 255.0f; - -Heightfield::Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color, - const QByteArray& material, const QVector& materials) : - _increment(increment), - _width((int)glm::round((bounds.maximum.x - bounds.minimum.x) / increment) + 1), - _heightScale((bounds.maximum.y - bounds.minimum.y) / EIGHT_BIT_MAXIMUM), - _height(height), - _color(color), - _material(material), - _materials(materials) { - - setBounds(bounds); -} - -bool Heightfield::hasOwnColors() const { - return true; -} - -bool Heightfield::hasOwnMaterials() const { - return true; -} - -QRgb Heightfield::getColorAt(const glm::vec3& point) { - glm::vec3 relative = (point - getBounds().minimum) / _increment; - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - const uchar* src = (const uchar*)_color.constData(); - const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES; - const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES; - glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); - } - return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); -} - -int Heightfield::getMaterialAt(const glm::vec3& point) { - glm::vec3 relative = (point - getBounds().minimum) / _increment; - const uchar* src = (const uchar*)_material.constData(); - return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)]; -} - -QVector& Heightfield::getMaterials() { - return _materials; -} - -bool Heightfield::contains(const glm::vec3& point) { - if (!getBounds().contains(point)) { - return false; - } - glm::vec3 relative = (point - getBounds().minimum) / _increment; - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - const uchar* src = (const uchar*)_height.constData(); - float upperLeft = src[floorZ * _width + floorX]; - float lowerRight = src[ceilZ * _width + ceilX]; - float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - float upperRight = src[floorZ * _width + ceilX]; - interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), - (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - float lowerLeft = src[ceilZ * _width + floorX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); - } - return interpolatedHeight != 0.0f && point.y <= interpolatedHeight * _heightScale + getBounds().minimum.y; -} - -bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - // find the initial location in heightfield coordinates - float rayDistance; - glm::vec3 direction = end - start; - if (!getBounds().findRayIntersection(start, direction, rayDistance) || rayDistance > 1.0f) { - return false; - } - glm::vec3 entry = start + direction * rayDistance; - const float DISTANCE_THRESHOLD = 0.001f; - if (glm::abs(entry.x - getBounds().minimum.x) < DISTANCE_THRESHOLD) { - normal = glm::vec3(-1.0f, 0.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.x - getBounds().maximum.x) < DISTANCE_THRESHOLD) { - normal = glm::vec3(1.0f, 0.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.y - getBounds().minimum.y) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, -1.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.y - getBounds().maximum.y) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 1.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.z - getBounds().minimum.z) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, -1.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.z - getBounds().maximum.z) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, 1.0f); - distance = rayDistance; - return true; - } - entry = (entry - getBounds().minimum) / _increment; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (direction.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (direction.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - bool withinBounds = true; - float accumulatedDistance = 0.0f; - const uchar* src = (const uchar*)_height.constData(); - int highestX = _width - 1; - float highestY = (getBounds().maximum.y - getBounds().minimum.y) / _increment; - int highestZ = (int)glm::round((getBounds().maximum.z - getBounds().minimum.z) / _increment); - float heightScale = _heightScale / _increment; - while (withinBounds && accumulatedDistance <= 1.0f) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, 0), highestX); - int floorZ = qMin(qMax((int)floors.z, 0), highestZ); - int ceilX = qMin(qMax((int)ceils.x, 0), highestX); - int ceilZ = qMin(qMax((int)ceils.z, 0), highestZ); - float upperLeft = src[floorZ * _width + floorX] * heightScale; - float upperRight = src[floorZ * _width + ceilX] * heightScale; - float lowerLeft = src[ceilZ * _width + floorX] * heightScale; - float lowerRight = src[ceilZ * _width + ceilX] * heightScale; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (direction.x > 0.0f) { - xDistance = (ceils.x - entry.x) / direction.x; - } else if (direction.x < 0.0f) { - xDistance = (floors.x - entry.x) / direction.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (direction.z > 0.0f) { - zDistance = (ceils.z - entry.z) / direction.z; - } else if (direction.z < 0.0f) { - zDistance = (floors.z - entry.z) / direction.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - withinBounds = false; // line points upwards/downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * direction; - withinBounds = (exit.y >= 0.0f && exit.y <= highestY); - if (exitDistance == xDistance) { - if (direction.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highestX; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (direction.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highestZ; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, direction); - if (lowerProduct != 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - distance = rayDistance + (accumulatedDistance + planeDistance) * _increment; - normal = glm::normalize(lowerNormal); - return true; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, direction); - if (upperProduct != 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - distance = rayDistance + (accumulatedDistance + planeDistance) * _increment; - normal = glm::normalize(upperNormal); - return true; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return false; -} diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 5033d116b0..b80da9b2a0 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -28,9 +28,7 @@ class MetavoxelNode; class MetavoxelRendererImplementation; class MetavoxelVisitation; class MetavoxelVisitor; -class NetworkValue; class Spanner; -class SpannerRenderer; /// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a /// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided. @@ -526,281 +524,4 @@ public: virtual QByteArray getImplementationClassName() const; }; -/// An object that spans multiple octree cells. -class Spanner : public SharedObject { - Q_OBJECT - Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) - Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) - Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) - -public: - - /// Returns the value of the global visit counter and increments it. - static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); } - - Spanner(); - - void setBounds(const Box& bounds); - const Box& getBounds() const { return _bounds; } - - void setPlacementGranularity(float granularity) { _placementGranularity = granularity; } - float getPlacementGranularity() const { return _placementGranularity; } - - void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } - float getVoxelizationGranularity() const { return _voxelizationGranularity; } - - void setMerged(bool merged) { _merged = merged; } - bool isMerged() const { return _merged; } - - /// Checks whether we've visited this object on the current traversal. If we have, returns false. - /// If we haven't, sets the last visit identifier and returns true. - bool testAndSetVisited(int visit); - - /// Returns a pointer to the renderer, creating it if necessary. - SpannerRenderer* getRenderer(); - - /// Finds the intersection between the described ray and this spanner. - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - - /// Checks whether this spanner has its own colors. - virtual bool hasOwnColors() const; - - /// Checks whether this spanner has its own materials. - virtual bool hasOwnMaterials() const; - - /// Checks whether the spanner contains the specified point. - virtual bool contains(const glm::vec3& point); - - /// Retrieves the color at the specified point. - virtual QRgb getColorAt(const glm::vec3& point); - - /// Retrieves the material at the specified point. - virtual int getMaterialAt(const glm::vec3& point); - - /// Retrieves a reference to the list of materials. - virtual QVector& getMaterials(); - - /// Finds the intersection, if any, between the specified line segment and the spanner. - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -signals: - - void boundsWillChange(); - void boundsChanged(const Box& bounds); - -protected: - - SpannerRenderer* _renderer; - - /// Returns the name of the class to instantiate in order to render this spanner. - virtual QByteArray getRendererClassName() const; - -private: - - Box _bounds; - float _placementGranularity; - float _voxelizationGranularity; - bool _merged; - QHash _lastVisits; ///< last visit identifiers for each thread - QMutex _lastVisitsMutex; - - static QAtomicInt _nextVisit; ///< the global visit counter -}; - -/// Base class for objects that can render spanners. -class SpannerRenderer : public QObject { - Q_OBJECT - -public: - - enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE }; - - Q_INVOKABLE SpannerRenderer(); - - virtual void init(Spanner* spanner); - virtual void simulate(float deltaTime); - virtual void render(const glm::vec4& color, Mode mode); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - -protected: - - Spanner* _spanner; -}; - -/// An object with a 3D transform. -class Transformable : public Spanner { - Q_OBJECT - Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged) - Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged) - Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged) - -public: - - Transformable(); - - void setTranslation(const glm::vec3& translation); - const glm::vec3& getTranslation() const { return _translation; } - - void setRotation(const glm::quat& rotation); - const glm::quat& getRotation() const { return _rotation; } - - void setScale(float scale); - float getScale() const { return _scale; } - -signals: - - void translationChanged(const glm::vec3& translation); - void rotationChanged(const glm::quat& rotation); - void scaleChanged(float scale); - -private: - - glm::vec3 _translation; - glm::quat _rotation; - float _scale; -}; - -/// A transformable object with a color. -class ColorTransformable : public Transformable { - Q_OBJECT - Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false) - -public: - - ColorTransformable(); - - void setColor(const QColor& color); - const QColor& getColor() const { return _color; } - -signals: - - void colorChanged(const QColor& color); - -protected: - - QColor _color; -}; - -/// A sphere. -class Sphere : public ColorTransformable { - Q_OBJECT - -public: - - Q_INVOKABLE Sphere(); - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -protected: - - virtual QByteArray getRendererClassName() const; - -private slots: - - void updateBounds(); -}; - -/// A cuboid. -class Cuboid : public ColorTransformable { - Q_OBJECT - Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) - Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) - -public: - - Q_INVOKABLE Cuboid(); - - void setAspectY(float aspectY); - float getAspectY() const { return _aspectY; } - - void setAspectZ(float aspectZ); - float getAspectZ() const { return _aspectZ; } - - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -signals: - - void aspectYChanged(float aspectY); - void aspectZChanged(float aspectZ); - -protected: - - virtual QByteArray getRendererClassName() const; - -private slots: - - void updateBoundsAndPlanes(); - -private: - - float _aspectY; - float _aspectZ; - - static const int PLANE_COUNT = 6; - glm::vec4 _planes[PLANE_COUNT]; -}; - -/// A static 3D model loaded from the network. -class StaticModel : public Transformable { - Q_OBJECT - Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged) - -public: - - Q_INVOKABLE StaticModel(); - - void setURL(const QUrl& url); - const QUrl& getURL() const { return _url; } - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - -signals: - - void urlChanged(const QUrl& url); - -protected: - - virtual QByteArray getRendererClassName() const; - -private: - - QUrl _url; -}; - -/// A heightfield represented as a spanner. -class Heightfield : public Transformable { - Q_OBJECT - -public: - - Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color, - const QByteArray& material, const QVector& materials); - - QByteArray& getHeight() { return _height; } - QByteArray& getColor() { return _color; } - QByteArray& getMaterial() { return _material; } - - virtual bool hasOwnColors() const; - virtual bool hasOwnMaterials() const; - virtual QRgb getColorAt(const glm::vec3& point); - virtual int getMaterialAt(const glm::vec3& point); - virtual QVector& getMaterials(); - - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -private: - - float _increment; - int _width; - float _heightScale; - QByteArray _height; - QByteArray _color; - QByteArray _material; - QVector _materials; -}; - #endif // hifi_MetavoxelData_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 64e7863dee..d40b49a2e6 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -10,6 +10,7 @@ // #include "MetavoxelMessages.h" +#include "Spanner.h" void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { static_cast(edit.data())->apply(data, objects); @@ -754,7 +755,7 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { } // create spanner if necessary - Heightfield* spanner = static_cast(_spanner.data()); + TempHeightfield* spanner = static_cast(_spanner.data()); float increment = 1.0f / heightScale; if (!spanner) { _spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment; @@ -767,7 +768,7 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { Box innerBounds = _spannerBounds; innerBounds.maximum.x -= increment; innerBounds.maximum.z -= increment; - _spanner = spanner = new Heightfield(innerBounds, increment, QByteArray(heightfieldArea, 0), + _spanner = spanner = new TempHeightfield(innerBounds, increment, QByteArray(heightfieldArea, 0), QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0), QVector()); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp new file mode 100644 index 0000000000..c6650ec33c --- /dev/null +++ b/libraries/metavoxels/src/Spanner.cpp @@ -0,0 +1,783 @@ +// +// Spanner.cpp +// libraries/metavoxels/src +// +// Created by Andrzej Kapolka on 11/10/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 +// + +#include + +#include + +#include + +#include "Spanner.h" + +REGISTER_META_OBJECT(Spanner) +REGISTER_META_OBJECT(Sphere) +REGISTER_META_OBJECT(Cuboid) +REGISTER_META_OBJECT(StaticModel) +REGISTER_META_OBJECT(Heightfield) + +static int heightfieldHeightTypeId = registerSimpleMetaType(); +static int heightfieldColorTypeId = registerSimpleMetaType(); +static int heightfieldMaterialTypeId = registerSimpleMetaType(); + +const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; +const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); + +Spanner::Spanner() : + _renderer(NULL), + _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), + _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), + _merged(false) { +} + +void Spanner::setBounds(const Box& bounds) { + if (_bounds == bounds) { + return; + } + emit boundsWillChange(); + emit boundsChanged(_bounds = bounds); +} + +bool Spanner::testAndSetVisited(int visit) { + QMutexLocker locker(&_lastVisitsMutex); + int& lastVisit = _lastVisits[QThread::currentThread()]; + if (lastVisit == visit) { + return false; + } + lastVisit = visit; + return true; +} + +SpannerRenderer* Spanner::getRenderer() { + if (!_renderer) { + QByteArray className = getRendererClassName(); + const QMetaObject* metaObject = Bitstream::getMetaObject(className); + if (!metaObject) { + qDebug() << "Unknown class name:" << className; + metaObject = &SpannerRenderer::staticMetaObject; + } + _renderer = static_cast(metaObject->newInstance()); + connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater); + _renderer->init(this); + } + return _renderer; +} + +bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return _bounds.findRayIntersection(origin, direction, distance); +} + +bool Spanner::hasOwnColors() const { + return false; +} + +bool Spanner::hasOwnMaterials() const { + return false; +} + +QRgb Spanner::getColorAt(const glm::vec3& point) { + return 0; +} + +int Spanner::getMaterialAt(const glm::vec3& point) { + return 0; +} + +QVector& Spanner::getMaterials() { + static QVector emptyMaterials; + return emptyMaterials; +} + +bool Spanner::contains(const glm::vec3& point) { + return false; +} + +bool Spanner::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { + return false; +} + +QByteArray Spanner::getRendererClassName() const { + return "SpannerRendererer"; +} + +QAtomicInt Spanner::_nextVisit(1); + +SpannerRenderer::SpannerRenderer() { +} + +void SpannerRenderer::init(Spanner* spanner) { + _spanner = spanner; +} + +void SpannerRenderer::simulate(float deltaTime) { + // nothing by default +} + +void SpannerRenderer::render(const glm::vec4& color, Mode mode) { + // nothing by default +} + +bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return false; +} + +Transformable::Transformable() : _scale(1.0f) { +} + +void Transformable::setTranslation(const glm::vec3& translation) { + if (_translation != translation) { + emit translationChanged(_translation = translation); + } +} + +void Transformable::setRotation(const glm::quat& rotation) { + if (_rotation != rotation) { + emit rotationChanged(_rotation = rotation); + } +} + +void Transformable::setScale(float scale) { + if (_scale != scale) { + emit scaleChanged(_scale = scale); + } +} + +ColorTransformable::ColorTransformable() : + _color(Qt::white) { +} + +void ColorTransformable::setColor(const QColor& color) { + if (_color != color) { + emit colorChanged(_color = color); + } +} + +Sphere::Sphere() { + connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds())); + connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds())); + updateBounds(); +} + +bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); +} + +bool Sphere::contains(const glm::vec3& point) { + return glm::distance(point, getTranslation()) <= getScale(); +} + +bool Sphere::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { + glm::vec3 relativeStart = start - getTranslation(); + glm::vec3 vector = end - start; + float a = glm::dot(vector, vector); + if (a == 0.0f) { + return false; + } + float b = glm::dot(relativeStart, vector); + float radicand = b * b - a * (glm::dot(relativeStart, relativeStart) - getScale() * getScale()); + if (radicand < 0.0f) { + return false; + } + float radical = glm::sqrt(radicand); + float first = (-b - radical) / a; + if (first >= 0.0f && first <= 1.0f) { + distance = first; + normal = glm::normalize(relativeStart + vector * distance); + return true; + } + float second = (-b + radical) / a; + if (second >= 0.0f && second <= 1.0f) { + distance = second; + normal = glm::normalize(relativeStart + vector * distance); + return true; + } + return false; +} + +QByteArray Sphere::getRendererClassName() const { + return "SphereRenderer"; +} + +void Sphere::updateBounds() { + glm::vec3 extent(getScale(), getScale(), getScale()); + setBounds(Box(getTranslation() - extent, getTranslation() + extent)); +} + +Cuboid::Cuboid() : + _aspectY(1.0f), + _aspectZ(1.0f) { + + connect(this, &Cuboid::translationChanged, this, &Cuboid::updateBoundsAndPlanes); + connect(this, &Cuboid::rotationChanged, this, &Cuboid::updateBoundsAndPlanes); + connect(this, &Cuboid::scaleChanged, this, &Cuboid::updateBoundsAndPlanes); + connect(this, &Cuboid::aspectYChanged, this, &Cuboid::updateBoundsAndPlanes); + connect(this, &Cuboid::aspectZChanged, this, &Cuboid::updateBoundsAndPlanes); + updateBoundsAndPlanes(); +} + +void Cuboid::setAspectY(float aspectY) { + if (_aspectY != aspectY) { + emit aspectYChanged(_aspectY = aspectY); + } +} + +void Cuboid::setAspectZ(float aspectZ) { + if (_aspectZ != aspectZ) { + emit aspectZChanged(_aspectZ = aspectZ); + } +} + +bool Cuboid::contains(const glm::vec3& point) { + glm::vec4 point4(point, 1.0f); + for (int i = 0; i < PLANE_COUNT; i++) { + if (glm::dot(_planes[i], point4) > 0.0f) { + return false; + } + } + return true; +} + +bool Cuboid::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { + glm::vec4 start4(start, 1.0f); + glm::vec4 vector = glm::vec4(end - start, 0.0f); + for (int i = 0; i < PLANE_COUNT; i++) { + // first check the segment against the plane + float divisor = glm::dot(_planes[i], vector); + if (glm::abs(divisor) < EPSILON) { + continue; + } + float t = -glm::dot(_planes[i], start4) / divisor; + if (t < 0.0f || t > 1.0f) { + continue; + } + // now that we've established that it intersects the plane, check against the other sides + glm::vec4 point = start4 + vector * t; + const int PLANES_PER_AXIS = 2; + int indexOffset = ((i / PLANES_PER_AXIS) + 1) * PLANES_PER_AXIS; + for (int j = 0; j < PLANE_COUNT - PLANES_PER_AXIS; j++) { + if (glm::dot(_planes[(indexOffset + j) % PLANE_COUNT], point) > 0.0f) { + goto outerContinue; + } + } + distance = t; + normal = glm::vec3(_planes[i]); + return true; + + outerContinue: ; + } + return false; +} + +QByteArray Cuboid::getRendererClassName() const { + return "CuboidRenderer"; +} + +void Cuboid::updateBoundsAndPlanes() { + glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ); + glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); + setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent)); + + glm::vec4 translation4 = glm::vec4(getTranslation(), 1.0f); + _planes[0] = glm::vec4(glm::vec3(rotationMatrix[0]), -glm::dot(rotationMatrix[0], translation4) - getScale()); + _planes[1] = glm::vec4(glm::vec3(-rotationMatrix[0]), glm::dot(rotationMatrix[0], translation4) - getScale()); + _planes[2] = glm::vec4(glm::vec3(rotationMatrix[1]), -glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY); + _planes[3] = glm::vec4(glm::vec3(-rotationMatrix[1]), glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY); + _planes[4] = glm::vec4(glm::vec3(rotationMatrix[2]), -glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ); + _planes[5] = glm::vec4(glm::vec3(-rotationMatrix[2]), glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ); +} + +StaticModel::StaticModel() { +} + +void StaticModel::setURL(const QUrl& url) { + if (_url != url) { + emit urlChanged(_url = url); + } +} + +bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + // delegate to renderer, if we have one + return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : + Spanner::findRayIntersection(origin, direction, distance); +} + +QByteArray StaticModel::getRendererClassName() const { + return "StaticModelRenderer"; +} + +const float EIGHT_BIT_MAXIMUM = 255.0f; + +TempHeightfield::TempHeightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color, + const QByteArray& material, const QVector& materials) : + _increment(increment), + _width((int)glm::round((bounds.maximum.x - bounds.minimum.x) / increment) + 1), + _heightScale((bounds.maximum.y - bounds.minimum.y) / EIGHT_BIT_MAXIMUM), + _height(height), + _color(color), + _material(material), + _materials(materials) { + + setBounds(bounds); +} + +bool TempHeightfield::hasOwnColors() const { + return true; +} + +bool TempHeightfield::hasOwnMaterials() const { + return true; +} + +QRgb TempHeightfield::getColorAt(const glm::vec3& point) { + glm::vec3 relative = (point - getBounds().minimum) / _increment; + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = (int)floors.x; + int floorZ = (int)floors.z; + int ceilX = (int)ceils.x; + int ceilZ = (int)ceils.z; + const uchar* src = (const uchar*)_color.constData(); + const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES; + const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES; + glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); + } + return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); +} + +int TempHeightfield::getMaterialAt(const glm::vec3& point) { + glm::vec3 relative = (point - getBounds().minimum) / _increment; + const uchar* src = (const uchar*)_material.constData(); + return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)]; +} + +QVector& TempHeightfield::getMaterials() { + return _materials; +} + +bool TempHeightfield::contains(const glm::vec3& point) { + if (!getBounds().contains(point)) { + return false; + } + glm::vec3 relative = (point - getBounds().minimum) / _increment; + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = (int)floors.x; + int floorZ = (int)floors.z; + int ceilX = (int)ceils.x; + int ceilZ = (int)ceils.z; + const uchar* src = (const uchar*)_height.constData(); + float upperLeft = src[floorZ * _width + floorX]; + float lowerRight = src[ceilZ * _width + ceilX]; + float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + float upperRight = src[floorZ * _width + ceilX]; + interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), + (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + float lowerLeft = src[ceilZ * _width + floorX]; + interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); + } + return interpolatedHeight != 0.0f && point.y <= interpolatedHeight * _heightScale + getBounds().minimum.y; +} + +bool TempHeightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { + // find the initial location in heightfield coordinates + float rayDistance; + glm::vec3 direction = end - start; + if (!getBounds().findRayIntersection(start, direction, rayDistance) || rayDistance > 1.0f) { + return false; + } + glm::vec3 entry = start + direction * rayDistance; + const float DISTANCE_THRESHOLD = 0.001f; + if (glm::abs(entry.x - getBounds().minimum.x) < DISTANCE_THRESHOLD) { + normal = glm::vec3(-1.0f, 0.0f, 0.0f); + distance = rayDistance; + return true; + + } else if (glm::abs(entry.x - getBounds().maximum.x) < DISTANCE_THRESHOLD) { + normal = glm::vec3(1.0f, 0.0f, 0.0f); + distance = rayDistance; + return true; + + } else if (glm::abs(entry.y - getBounds().minimum.y) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, -1.0f, 0.0f); + distance = rayDistance; + return true; + + } else if (glm::abs(entry.y - getBounds().maximum.y) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, 1.0f, 0.0f); + distance = rayDistance; + return true; + + } else if (glm::abs(entry.z - getBounds().minimum.z) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, 0.0f, -1.0f); + distance = rayDistance; + return true; + + } else if (glm::abs(entry.z - getBounds().maximum.z) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, 0.0f, 1.0f); + distance = rayDistance; + return true; + } + entry = (entry - getBounds().minimum) / _increment; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (direction.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (direction.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + + bool withinBounds = true; + float accumulatedDistance = 0.0f; + const uchar* src = (const uchar*)_height.constData(); + int highestX = _width - 1; + float highestY = (getBounds().maximum.y - getBounds().minimum.y) / _increment; + int highestZ = (int)glm::round((getBounds().maximum.z - getBounds().minimum.z) / _increment); + float heightScale = _heightScale / _increment; + while (withinBounds && accumulatedDistance <= 1.0f) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, 0), highestX); + int floorZ = qMin(qMax((int)floors.z, 0), highestZ); + int ceilX = qMin(qMax((int)ceils.x, 0), highestX); + int ceilZ = qMin(qMax((int)ceils.z, 0), highestZ); + float upperLeft = src[floorZ * _width + floorX] * heightScale; + float upperRight = src[floorZ * _width + ceilX] * heightScale; + float lowerLeft = src[ceilZ * _width + floorX] * heightScale; + float lowerRight = src[ceilZ * _width + ceilX] * heightScale; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (direction.x > 0.0f) { + xDistance = (ceils.x - entry.x) / direction.x; + } else if (direction.x < 0.0f) { + xDistance = (floors.x - entry.x) / direction.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (direction.z > 0.0f) { + zDistance = (ceils.z - entry.z) / direction.z; + } else if (direction.z < 0.0f) { + zDistance = (floors.z - entry.z) / direction.z; + } + + // the exit distance is the lower of those two + float exitDistance = qMin(xDistance, zDistance); + glm::vec3 exit, nextFloors = floors, nextCeils = ceils; + if (exitDistance == FLT_MAX) { + withinBounds = false; // line points upwards/downwards; check this cell only + + } else { + // find the exit point and the next cell, and determine whether it's still within the bounds + exit = entry + exitDistance * direction; + withinBounds = (exit.y >= 0.0f && exit.y <= highestY); + if (exitDistance == xDistance) { + if (direction.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highestX; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (direction.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highestZ; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f; + nextCeils.z -= 1.0f; + } + } + // check the vertical range of the ray against the ranges of the cell heights + if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || + qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + continue; + } + } + // having passed the bounds check, we must check against the planes + glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); + + // first check the triangle including the Z+ segment + glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); + float lowerProduct = glm::dot(lowerNormal, direction); + if (lowerProduct != 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * direction; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + distance = rayDistance + (accumulatedDistance + planeDistance) * _increment; + normal = glm::normalize(lowerNormal); + return true; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, direction); + if (upperProduct != 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * direction; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + distance = rayDistance + (accumulatedDistance + planeDistance) * _increment; + normal = glm::normalize(upperNormal); + return true; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return false; +} + +HeightfieldData::HeightfieldData(int width) : + _width(width) { +} + +HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents) : + HeightfieldData(width), + _contents(contents) { +} + +HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) { +} + +HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) { +} + +void HeightfieldHeight::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) { +} + +Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) { + if (value) { + value->write(out); + } else { + out << 0; + } + return out; +} + +Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value) { + int size; + in >> size; + if (size == 0) { + value = HeightfieldHeightPointer(); + } else { + value = new HeightfieldHeight(0, QVector()); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { +} + +template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { +} + +HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : + HeightfieldData(width), + _contents(contents) { +} + +HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) { +} + +HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) { +} + +void HeightfieldColor::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) { +} + +Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) { + if (value) { + value->write(out); + } else { + out << 0; + } + return out; +} + +Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value) { + int size; + in >> size; + if (size == 0) { + value = HeightfieldColorPointer(); + } else { + value = new HeightfieldColor(0, QByteArray()); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { + +} + +template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { +} + +HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, + const QVector& materials) : + HeightfieldData(width), + _contents(contents), + _materials(materials) { +} + +HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) { +} + +HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) { +} + +void HeightfieldMaterial::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + + } + out << _encoded.size(); + out.writeAligned(_encoded); + out << _materials; +} + +void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) { +} + +Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value) { + if (value) { + value->write(out); + } else { + out << 0; + } + return out; +} + +Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value) { + int size; + in >> size; + if (size == 0) { + value = HeightfieldMaterialPointer(); + } else { + value = new HeightfieldMaterial(0, QByteArray(), QVector()); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, + const HeightfieldMaterialPointer& reference) { +} + +template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) { +} + +Heightfield::Heightfield() : + _aspectY(1.0f), + _aspectZ(1.0f) { + + connect(this, &Heightfield::translationChanged, this, &Heightfield::updateBounds); + connect(this, &Heightfield::rotationChanged, this, &Heightfield::updateBounds); + connect(this, &Heightfield::scaleChanged, this, &Heightfield::updateBounds); + connect(this, &Heightfield::aspectYChanged, this, &Heightfield::updateBounds); + connect(this, &Heightfield::aspectZChanged, this, &Heightfield::updateBounds); + updateBounds(); +} + +void Heightfield::setAspectY(float aspectY) { + if (_aspectY != aspectY) { + emit aspectYChanged(_aspectY = aspectY); + } +} + +void Heightfield::setAspectZ(float aspectZ) { + if (_aspectZ != aspectZ) { + emit aspectZChanged(_aspectZ = aspectZ); + } +} + +void Heightfield::setHeight(const HeightfieldHeightPointer& height) { + if (_height != height) { + emit heightChanged(_height = height); + } +} + +void Heightfield::setColor(const HeightfieldColorPointer& color) { + if (_color != color) { + emit colorChanged(_color = color); + } +} + +void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) { + if (_material != material) { + emit materialChanged(_material = material); + } +} + +QByteArray Heightfield::getRendererClassName() const { + return "HeightfieldRenderer"; +} + +void Heightfield::updateBounds() { + glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ); + glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); + setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent)); +} diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h new file mode 100644 index 0000000000..e8ef2f30c1 --- /dev/null +++ b/libraries/metavoxels/src/Spanner.h @@ -0,0 +1,454 @@ +// +// Spanner.h +// libraries/metavoxels/src +// +// Created by Andrzej Kapolka on 11/10/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 +// + +#ifndef hifi_Spanner_h +#define hifi_Spanner_h + +#include + +#include "AttributeRegistry.h" +#include "MetavoxelUtil.h" + +class HeightfieldColor; +class HeightfieldHeight; +class HeightfieldMaterial; +class SpannerRenderer; + +/// An object that spans multiple octree cells. +class Spanner : public SharedObject { + Q_OBJECT + Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) + Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) + Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) + +public: + + /// Returns the value of the global visit counter and increments it. + static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); } + + Spanner(); + + void setBounds(const Box& bounds); + const Box& getBounds() const { return _bounds; } + + void setPlacementGranularity(float granularity) { _placementGranularity = granularity; } + float getPlacementGranularity() const { return _placementGranularity; } + + void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } + float getVoxelizationGranularity() const { return _voxelizationGranularity; } + + void setMerged(bool merged) { _merged = merged; } + bool isMerged() const { return _merged; } + + /// Checks whether we've visited this object on the current traversal. If we have, returns false. + /// If we haven't, sets the last visit identifier and returns true. + bool testAndSetVisited(int visit); + + /// Returns a pointer to the renderer, creating it if necessary. + SpannerRenderer* getRenderer(); + + /// Finds the intersection between the described ray and this spanner. + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + + /// Checks whether this spanner has its own colors. + virtual bool hasOwnColors() const; + + /// Checks whether this spanner has its own materials. + virtual bool hasOwnMaterials() const; + + /// Checks whether the spanner contains the specified point. + virtual bool contains(const glm::vec3& point); + + /// Retrieves the color at the specified point. + virtual QRgb getColorAt(const glm::vec3& point); + + /// Retrieves the material at the specified point. + virtual int getMaterialAt(const glm::vec3& point); + + /// Retrieves a reference to the list of materials. + virtual QVector& getMaterials(); + + /// Finds the intersection, if any, between the specified line segment and the spanner. + virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); + +signals: + + void boundsWillChange(); + void boundsChanged(const Box& bounds); + +protected: + + SpannerRenderer* _renderer; + + /// Returns the name of the class to instantiate in order to render this spanner. + virtual QByteArray getRendererClassName() const; + +private: + + Box _bounds; + float _placementGranularity; + float _voxelizationGranularity; + bool _merged; + QHash _lastVisits; ///< last visit identifiers for each thread + QMutex _lastVisitsMutex; + + static QAtomicInt _nextVisit; ///< the global visit counter +}; + +/// Base class for objects that can render spanners. +class SpannerRenderer : public QObject { + Q_OBJECT + +public: + + enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE }; + + Q_INVOKABLE SpannerRenderer(); + + virtual void init(Spanner* spanner); + virtual void simulate(float deltaTime); + virtual void render(const glm::vec4& color, Mode mode); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + +protected: + + Spanner* _spanner; +}; + +/// An object with a 3D transform. +class Transformable : public Spanner { + Q_OBJECT + Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged) + Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged) + +public: + + Transformable(); + + void setTranslation(const glm::vec3& translation); + const glm::vec3& getTranslation() const { return _translation; } + + void setRotation(const glm::quat& rotation); + const glm::quat& getRotation() const { return _rotation; } + + void setScale(float scale); + float getScale() const { return _scale; } + +signals: + + void translationChanged(const glm::vec3& translation); + void rotationChanged(const glm::quat& rotation); + void scaleChanged(float scale); + +private: + + glm::vec3 _translation; + glm::quat _rotation; + float _scale; +}; + +/// A transformable object with a color. +class ColorTransformable : public Transformable { + Q_OBJECT + Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false) + +public: + + ColorTransformable(); + + void setColor(const QColor& color); + const QColor& getColor() const { return _color; } + +signals: + + void colorChanged(const QColor& color); + +protected: + + QColor _color; +}; + +/// A sphere. +class Sphere : public ColorTransformable { + Q_OBJECT + +public: + + Q_INVOKABLE Sphere(); + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual bool contains(const glm::vec3& point); + virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); + +protected: + + virtual QByteArray getRendererClassName() const; + +private slots: + + void updateBounds(); +}; + +/// A cuboid. +class Cuboid : public ColorTransformable { + Q_OBJECT + Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) + Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) + +public: + + Q_INVOKABLE Cuboid(); + + void setAspectY(float aspectY); + float getAspectY() const { return _aspectY; } + + void setAspectZ(float aspectZ); + float getAspectZ() const { return _aspectZ; } + + virtual bool contains(const glm::vec3& point); + virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); + +signals: + + void aspectYChanged(float aspectY); + void aspectZChanged(float aspectZ); + +protected: + + virtual QByteArray getRendererClassName() const; + +private slots: + + void updateBoundsAndPlanes(); + +private: + + float _aspectY; + float _aspectZ; + + static const int PLANE_COUNT = 6; + glm::vec4 _planes[PLANE_COUNT]; +}; + +/// A static 3D model loaded from the network. +class StaticModel : public Transformable { + Q_OBJECT + Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged) + +public: + + Q_INVOKABLE StaticModel(); + + void setURL(const QUrl& url); + const QUrl& getURL() const { return _url; } + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + +signals: + + void urlChanged(const QUrl& url); + +protected: + + virtual QByteArray getRendererClassName() const; + +private: + + QUrl _url; +}; + +/// A heightfield represented as a spanner. +class TempHeightfield : public Transformable { + Q_OBJECT + +public: + + TempHeightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color, + const QByteArray& material, const QVector& materials); + + QByteArray& getHeight() { return _height; } + QByteArray& getColor() { return _color; } + QByteArray& getMaterial() { return _material; } + + virtual bool hasOwnColors() const; + virtual bool hasOwnMaterials() const; + virtual QRgb getColorAt(const glm::vec3& point); + virtual int getMaterialAt(const glm::vec3& point); + virtual QVector& getMaterials(); + + virtual bool contains(const glm::vec3& point); + virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); + +private: + + float _increment; + int _width; + float _heightScale; + QByteArray _height; + QByteArray _color; + QByteArray _material; + QVector _materials; +}; + +/// Base class for heightfield data blocks. +class HeightfieldData : public DataBlock { +public: + + HeightfieldData(int width = 0); + + int getWidth() const { return _width; } + +protected: + + int _width; +}; + +typedef QExplicitlySharedDataPointer HeightfieldHeightPointer; + +/// A block of height data associated with a heightfield. +class HeightfieldHeight : public HeightfieldData { +public: + + HeightfieldHeight(int width, const QVector& contents); + HeightfieldHeight(Bitstream& in, int bytes); + HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference); + + const QVector& getContents() const { return _contents; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference); + +private: + + QVector _contents; +}; + +Q_DECLARE_METATYPE(HeightfieldHeightPointer) + +Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value); +Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value); + +template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference); +template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference); + +typedef QExplicitlySharedDataPointer HeightfieldColorPointer; + +/// A block of color data associated with a heightfield. +class HeightfieldColor : public HeightfieldData { +public: + + HeightfieldColor(int width, const QByteArray& contents); + HeightfieldColor(Bitstream& in, int bytes); + HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference); + + const QByteArray& getContents() const { return _contents; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldColorPointer& reference); + +private: + + QByteArray _contents; +}; + +Q_DECLARE_METATYPE(HeightfieldColorPointer) + +Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value); +Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value); + +template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference); +template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference); + +typedef QExplicitlySharedDataPointer HeightfieldMaterialPointer; + +/// A block of material data associated with a heightfield. +class HeightfieldMaterial : public HeightfieldData { +public: + + HeightfieldMaterial(int width, const QByteArray& contents, const QVector& materials); + HeightfieldMaterial(Bitstream& in, int bytes); + HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference); + + const QByteArray& getContents() const { return _contents; } + const QVector& getMaterials() const { return _materials; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference); + +private: + + QByteArray _contents; + QVector _materials; +}; + +Q_DECLARE_METATYPE(HeightfieldMaterialPointer) + +Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value); +Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value); + +template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); +template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); + +/// A heightfield represented as a spanner. +class Heightfield : public Transformable { + Q_OBJECT + Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) + Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) + Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged) + Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged) + +public: + + Q_INVOKABLE Heightfield(); + + void setAspectY(float aspectY); + float getAspectY() const { return _aspectY; } + + void setAspectZ(float aspectZ); + float getAspectZ() const { return _aspectZ; } + + void setHeight(const HeightfieldHeightPointer& height); + const HeightfieldHeightPointer& getHeight() const { return _height; } + + void setColor(const HeightfieldColorPointer& color); + const HeightfieldColorPointer& getColor() const { return _color; } + + void setMaterial(const HeightfieldMaterialPointer& material); + const HeightfieldMaterialPointer& getMaterial() const { return _material; } + +signals: + + void aspectYChanged(float aspectY); + void aspectZChanged(float aspectZ); + void heightChanged(const HeightfieldHeightPointer& height); + void colorChanged(const HeightfieldColorPointer& color); + void materialChanged(const HeightfieldMaterialPointer& material); + +protected: + + virtual QByteArray getRendererClassName() const; + +private slots: + + void updateBounds(); + +private: + + float _aspectY; + float _aspectZ; + HeightfieldHeightPointer _height; + HeightfieldColorPointer _color; + HeightfieldMaterialPointer _material; +}; + +#endif // hifi_Spanner_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index b5cf84ee28..8a274ae444 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 8; + return 9; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: From b94dab0e8f490f766d16a301e28f6ae35de35815 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Nov 2014 18:06:38 -0800 Subject: [PATCH 05/38] More work on heightfield spanners. --- libraries/metavoxels/src/MetavoxelUtil.cpp | 24 +-- libraries/metavoxels/src/MetavoxelUtil.h | 26 ++++ libraries/metavoxels/src/Spanner.cpp | 170 +++++++++++++++++++++ libraries/metavoxels/src/Spanner.h | 67 +++++++- 4 files changed, 263 insertions(+), 24 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 4aff339738..cdeec61500 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -77,32 +76,11 @@ QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const { return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName; } -static QItemEditorFactory* getItemEditorFactory() { +QItemEditorFactory* getItemEditorFactory() { static QItemEditorFactory* factory = new DelegatingItemEditorFactory(); return factory; } -/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create, -/// this class simply delays the value property name lookup until actually requested. -template class LazyItemEditorCreator : public QItemEditorCreatorBase { -public: - - virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); } - - virtual QByteArray valuePropertyName() const; - -protected: - - QByteArray _valuePropertyName; -}; - -template QByteArray LazyItemEditorCreator::valuePropertyName() const { - if (_valuePropertyName.isNull()) { - const_cast*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name(); - } - return _valuePropertyName; -} - static QItemEditorCreatorBase* createDoubleEditorCreator() { QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 4228af059f..3be54ca60d 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ class QByteArray; class QDoubleSpinBox; +class QItemEditorFactory; class QPushButton; class NetworkProgram; @@ -108,6 +110,30 @@ private: AxisExtents _crossProductExtents[CROSS_PRODUCT_EXTENT_COUNT]; }; +/// Returns a pointer to the singleton item editor factory. +QItemEditorFactory* getItemEditorFactory(); + +/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create, +/// this class simply delays the value property name lookup until actually requested. +template class LazyItemEditorCreator : public QItemEditorCreatorBase { +public: + + virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); } + + virtual QByteArray valuePropertyName() const; + +protected: + + QByteArray _valuePropertyName; +}; + +template QByteArray LazyItemEditorCreator::valuePropertyName() const { + if (_valuePropertyName.isNull()) { + const_cast*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name(); + } + return _valuePropertyName; +} + /// Editor for meta-object values. class QMetaObjectEditor : public QWidget { Q_OBJECT diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index c6650ec33c..50d5e31f56 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -9,6 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + +#include +#include +#include +#include +#include +#include #include #include @@ -17,6 +25,8 @@ #include "Spanner.h" +using namespace std; + REGISTER_META_OBJECT(Spanner) REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(Cuboid) @@ -27,6 +37,21 @@ static int heightfieldHeightTypeId = registerSimpleMetaType(); static int heightfieldMaterialTypeId = registerSimpleMetaType(); +static QItemEditorCreatorBase* createHeightfieldHeightEditorCreator() { + QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + +static QItemEditorCreatorBase* createHeightfieldColorEditorCreator() { + QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + +static QItemEditorCreatorBase* heightfieldHeightEditorCreator = createHeightfieldHeightEditorCreator(); +static QItemEditorCreatorBase* heightfieldColorEditorCreator = createHeightfieldColorEditorCreator(); + const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); @@ -627,6 +652,99 @@ template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { } +HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) : + QWidget(parent) { + + QHBoxLayout* layout = new QHBoxLayout(); + setLayout(layout); + + layout->addWidget(_select = new QPushButton("Select")); + connect(_select, &QPushButton::clicked, this, &HeightfieldHeightEditor::select); + + layout->addWidget(_clear = new QPushButton("Clear")); + connect(_clear, &QPushButton::clicked, this, &HeightfieldHeightEditor::clear); + _clear->setEnabled(false); +} + +void HeightfieldHeightEditor::setHeight(const HeightfieldHeightPointer& height) { + if ((_height = height)) { + _clear->setEnabled(true); + } else { + _clear->setEnabled(false); + } +} + +static int getHeightfieldSize(int size) { + return (int)glm::pow(2.0f, glm::round(glm::log((float)size - HeightfieldData::SHARED_EDGE) / + glm::log(2.0f))) + HeightfieldData::SHARED_EDGE; +} + +void HeightfieldHeightEditor::select() { + QSettings settings; + QString result = QFileDialog::getOpenFileName(this, "Select Height Image", settings.value("heightDir").toString(), + "Images (*.png *.jpg *.bmp *.raw)"); + if (result.isNull()) { + return; + } + settings.setValue("heightDir", QFileInfo(result).path()); + QImage image; + if (!image.load(result)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + const quint16 CONVERSION_OFFSET = 1; + if (result.toLower().endsWith(".raw")) { + QFile input(result); + input.open(QIODevice::ReadOnly); + QDataStream in(&input); + in.setByteOrder(QDataStream::LittleEndian); + QVector rawContents; + while (!in.atEnd()) { + quint16 height; + in >> height; + rawContents.append(height); + } + if (rawContents.isEmpty()) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + int rawSize = glm::sqrt((float)rawContents.size()); + int size = getHeightfieldSize(rawSize) + 2 * HeightfieldHeight::HEIGHT_BORDER; + QVector contents(size * size); + quint16* dest = contents.data() + (size + 1) * HeightfieldHeight::HEIGHT_BORDER; + const quint16* src = rawContents.constData(); + const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); + for (int i = 0; i < rawSize; i++, dest += size) { + for (quint16* lineDest = dest, *end = dest + rawSize; lineDest != end; lineDest++, src++) { + *lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET; + } + } + emit heightChanged(_height = new HeightfieldHeight(size, contents)); + _clear->setEnabled(true); + return; + } + image = image.convertToFormat(QImage::Format_RGB888); + int width = getHeightfieldSize(image.width()) + 2 * HeightfieldHeight::HEIGHT_BORDER; + int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER; + QVector contents(width * height); + quint16* dest = contents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; + const float CONVERSION_SCALE = 65534.0f / EIGHT_BIT_MAXIMUM; + for (int i = 0; i < image.height(); i++, dest += width) { + const uchar* src = image.constScanLine(i); + for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++, + src += DataBlock::COLOR_BYTES) { + *lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET; + } + } + emit heightChanged(_height = new HeightfieldHeight(width, contents)); + _clear->setEnabled(true); +} + +void HeightfieldHeightEditor::clear() { + emit heightChanged(_height = HeightfieldHeightPointer()); + _clear->setEnabled(false); +} + HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : HeightfieldData(width), _contents(contents) { @@ -677,6 +795,58 @@ template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, c template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { } +HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) : + QWidget(parent) { + + QHBoxLayout* layout = new QHBoxLayout(); + setLayout(layout); + + layout->addWidget(_select = new QPushButton("Select")); + connect(_select, &QPushButton::clicked, this, &HeightfieldColorEditor::select); + + layout->addWidget(_clear = new QPushButton("Clear")); + connect(_clear, &QPushButton::clicked, this, &HeightfieldColorEditor::clear); + _clear->setEnabled(false); +} + +void HeightfieldColorEditor::setColor(const HeightfieldColorPointer& color) { + if ((_color = color)) { + _clear->setEnabled(true); + } else { + _clear->setEnabled(false); + } +} + +void HeightfieldColorEditor::select() { + QSettings settings; + QString result = QFileDialog::getOpenFileName(this, "Select Color Image", settings.value("heightDir").toString(), + "Images (*.png *.jpg *.bmp)"); + if (result.isNull()) { + return; + } + settings.setValue("heightDir", QFileInfo(result).path()); + QImage image; + if (!image.load(result)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + image = image.convertToFormat(QImage::Format_RGB888); + int width = getHeightfieldSize(image.width()); + int height = getHeightfieldSize(image.height()); + QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0); + char* dest = contents.data(); + for (int i = 0; i < image.height(); i++, dest += width * DataBlock::COLOR_BYTES) { + memcpy(dest, image.constScanLine(i), image.width() * DataBlock::COLOR_BYTES); + } + emit colorChanged(_color = new HeightfieldColor(width, contents)); + _clear->setEnabled(true); +} + +void HeightfieldColorEditor::clear() { + emit colorChanged(_color = HeightfieldColorPointer()); + _clear->setEnabled(false); +} + HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, const QVector& materials) : HeightfieldData(width), diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index e8ef2f30c1..44f400887a 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -303,6 +303,8 @@ private: class HeightfieldData : public DataBlock { public: + static const int SHARED_EDGE = 1; + HeightfieldData(int width = 0); int getWidth() const { return _width; } @@ -318,6 +320,9 @@ typedef QExplicitlySharedDataPointer HeightfieldHeightPointer class HeightfieldHeight : public HeightfieldData { public: + static const int HEIGHT_BORDER = 1; + static const int HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER; + HeightfieldHeight(int width, const QVector& contents); HeightfieldHeight(Bitstream& in, int bytes); HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference); @@ -340,6 +345,36 @@ Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value); template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference); template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference); +/// Allows editing heightfield height blocks. +class HeightfieldHeightEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged USER true) + +public: + + HeightfieldHeightEditor(QWidget* parent = NULL); + +signals: + + void heightChanged(const HeightfieldHeightPointer& height); + +public slots: + + void setHeight(const HeightfieldHeightPointer& height); + +private slots: + + void select(); + void clear(); + +private: + + HeightfieldHeightPointer _height; + + QPushButton* _select; + QPushButton* _clear; +}; + typedef QExplicitlySharedDataPointer HeightfieldColorPointer; /// A block of color data associated with a heightfield. @@ -368,6 +403,36 @@ Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value); template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference); template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference); +/// Allows editing heightfield color blocks. +class HeightfieldColorEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged USER true) + +public: + + HeightfieldColorEditor(QWidget* parent = NULL); + +signals: + + void colorChanged(const HeightfieldColorPointer& color); + +public slots: + + void setColor(const HeightfieldColorPointer& color); + +private slots: + + void select(); + void clear(); + +private: + + HeightfieldColorPointer _color; + + QPushButton* _select; + QPushButton* _clear; +}; + typedef QExplicitlySharedDataPointer HeightfieldMaterialPointer; /// A block of material data associated with a heightfield. @@ -405,7 +470,7 @@ class Heightfield : public Transformable { Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged) Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged) + Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged DESIGNABLE false) public: From 93a61dd0c2903049329a7192d339c305835a7bea Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Nov 2014 19:26:19 -0800 Subject: [PATCH 06/38] More heightfield bits. --- interface/src/MetavoxelSystem.cpp | 98 +++++++++++++++++++++++++++++++ interface/src/MetavoxelSystem.h | 18 ++++++ 2 files changed, 116 insertions(+) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d2b3e20122..eeb6e3fdef 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2973,7 +2973,105 @@ void StaticModelRenderer::applyURL(const QUrl& url) { } HeightfieldRenderer::HeightfieldRenderer() { + glGenTextures(1, &_heightTextureID); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + 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); + + glGenTextures(1, &_materialTextureID); + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +HeightfieldRenderer::~HeightfieldRenderer() { + glDeleteTextures(1, &_heightTextureID); + glDeleteTextures(1, &_colorTextureID); + glDeleteTextures(1, &_materialTextureID); +} + +void HeightfieldRenderer::init(Spanner* spanner) { + SpannerRenderer::init(spanner); + + Heightfield* heightfield = static_cast(spanner); + applyHeight(heightfield->getHeight()); + applyColor(heightfield->getColor()); + applyMaterial(heightfield->getMaterial()); + + connect(heightfield, &Heightfield::heightChanged, this, &HeightfieldRenderer::applyHeight); + connect(heightfield, &Heightfield::colorChanged, this, &HeightfieldRenderer::applyColor); + connect(heightfield, &Heightfield::materialChanged, this, &HeightfieldRenderer::applyMaterial); } void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { + +} + +void HeightfieldRenderer::applyHeight(const HeightfieldHeightPointer& height) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + if (height) { + const QVector& contents = height->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, height->getWidth(), contents.size() / height->getWidth(), 0, + GL_RED, GL_UNSIGNED_SHORT, contents.constData()); + + } else { + const quint16 ZERO_VALUE = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, 1, 1, 0, GL_RED, GL_UNSIGNED_SHORT, &ZERO_VALUE); + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +void HeightfieldRenderer::applyColor(const HeightfieldColorPointer& color) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + if (color) { + const QByteArray& contents = color->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, color->getWidth(), + contents.size() / (color->getWidth() * DataBlock::COLOR_BYTES), 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); + + } else { + const quint8 WHITE_COLOR[] = { 255, 255, 255 }; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +void HeightfieldRenderer::applyMaterial(const HeightfieldMaterialPointer& material) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + if (material) { + const QByteArray& contents = material->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, material->getWidth(), contents.size() / material->getWidth(), 0, + GL_RED, GL_UNSIGNED_BYTE, contents.constData()); + + const QVector& materials = material->getMaterials(); + _networkTextures.resize(materials.size()); + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& material = materials.at(i); + if (material) { + _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( + static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); + } else { + _networkTextures[i].clear(); + } + } + } else { + const quint8 ZERO_VALUE = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &ZERO_VALUE); + _networkTextures.clear(); + } + glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 12d5b5bca8..8986bc109a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -457,8 +457,26 @@ class HeightfieldRenderer : public SpannerRenderer { public: Q_INVOKABLE HeightfieldRenderer(); + virtual ~HeightfieldRenderer(); + virtual void init(Spanner* spanner); virtual void render(const glm::vec4& color, Mode mode); + +private slots: + + void applyHeight(const HeightfieldHeightPointer& height); + void applyColor(const HeightfieldColorPointer& color); + void applyMaterial(const HeightfieldMaterialPointer& material); + +private: + + GLuint _heightTextureID; + GLuint _colorTextureID; + GLuint _materialTextureID; + QVector _networkTextures; + + typedef QPair BufferPair; + static QHash _bufferPairs; }; #endif // hifi_MetavoxelSystem_h From caef237376888ecc121f058fca7d8b9ce28bd0e8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Nov 2014 14:40:05 -0800 Subject: [PATCH 07/38] More work on spannerizing heightfields. --- .../shaders/metavoxel_heightfield_base.vert | 20 +-- interface/src/MetavoxelSystem.cpp | 130 ++++++++++++++++++ interface/src/MetavoxelSystem.h | 3 +- interface/src/ui/MetavoxelEditor.cpp | 47 ++++--- interface/src/ui/MetavoxelEditor.h | 13 +- libraries/metavoxels/src/Spanner.cpp | 12 +- 6 files changed, 189 insertions(+), 36 deletions(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_base.vert b/interface/resources/shaders/metavoxel_heightfield_base.vert index 5486f5fa67..55bf758636 100644 --- a/interface/resources/shaders/metavoxel_heightfield_base.vert +++ b/interface/resources/shaders/metavoxel_heightfield_base.vert @@ -15,10 +15,10 @@ uniform sampler2D heightMap; // the distance between height points in texture space -uniform float heightScale; +uniform vec3 heightScale; // the scale between height and color textures -uniform float colorScale; +uniform vec2 colorScale; // the interpolated normal varying vec4 normal; @@ -26,14 +26,14 @@ varying vec4 normal; void main(void) { // transform and store the normal for interpolation vec2 heightCoord = gl_MultiTexCoord0.st; - vec4 neighborHeights = vec4(texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r, - texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r, - texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r, - texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r); - vec4 neighborsZero = step(1.0 / 255.0, neighborHeights); + vec4 neighborHeights = vec4(texture2D(heightMap, heightCoord - vec2(heightScale.s, 0.0)).r, + texture2D(heightMap, heightCoord + vec2(heightScale.s, 0.0)).r, + texture2D(heightMap, heightCoord - vec2(0.0, heightScale.t)).r, + texture2D(heightMap, heightCoord + vec2(0.0, heightScale.t)).r); + vec4 neighborsZero = step(1.0 / 65535.0, neighborHeights); normal = normalize(gl_ModelViewMatrix * vec4( - (neighborHeights.x - neighborHeights.y) * neighborsZero.x * neighborsZero.y, heightScale, - (neighborHeights.z - neighborHeights.w) * neighborsZero.z * neighborsZero.w, 0.0)); + heightScale.s * (neighborHeights.x - neighborHeights.y) * neighborsZero.x * neighborsZero.y, heightScale.p, + heightScale.t * (neighborHeights.z - neighborHeights.w) * neighborsZero.z * neighborsZero.w, 0.0)); // add the height to the position float height = texture2D(heightMap, heightCoord).r; @@ -43,5 +43,5 @@ void main(void) { gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); // pass along the scaled/offset texture coordinates - gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale; + gl_TexCoord[0] = vec4((heightCoord - heightScale.st) * colorScale, 0.0, 1.0); } diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index eeb6e3fdef..3d3f3de9f5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -3016,7 +3016,134 @@ void HeightfieldRenderer::init(Spanner* spanner) { } void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { + // create the buffer objects lazily + Heightfield* heightfield = static_cast(_spanner); + if (!heightfield->getHeight() || !Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { + return; + } + int width = heightfield->getHeight()->getWidth(); + int height = heightfield->getHeight()->getContents().size() / width; + int innerWidth = width - 2 * HeightfieldBuffer::HEIGHT_BORDER; + int innerHeight = height - 2 * HeightfieldBuffer::HEIGHT_BORDER; + int vertexCount = width * height; + int rows = height - 1; + int columns = width - 1; + int indexCount = rows * columns * 3 * 2; + BufferPair& bufferPair = _bufferPairs[IntPair(width, height)]; + if (!bufferPair.first.isCreated()) { + QVector vertices(vertexCount); + HeightfieldPoint* point = vertices.data(); + + float xStep = 1.0f / (innerWidth - 1); + float zStep = 1.0f / (innerHeight - 1); + float z = -zStep; + float sStep = 1.0f / innerWidth; + float tStep = 1.0f / innerHeight; + float t = -tStep / 2.0f; + for (int i = 0; i < height; i++, z += zStep, t += tStep) { + float x = -xStep; + float s = -sStep / 2.0f; + const float SKIRT_LENGTH = 0.25f; + float baseY = (i == 0 || i == height - 1) ? -SKIRT_LENGTH : 0.0f; + for (int j = 0; j < width; j++, point++, x += xStep, s += sStep) { + point->vertex = glm::vec3(x, (j == 0 || j == width - 1) ? -SKIRT_LENGTH : baseY, z); + point->textureCoord = glm::vec2(s, t); + } + } + + 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 * width; + int nextLineIndex = (i + 1) * width; + for (int j = 0; j < columns; j++) { + *index++ = lineIndex + j; + *index++ = nextLineIndex + j; + *index++ = nextLineIndex + j + 1; + + *index++ = nextLineIndex + j + 1; + *index++ = lineIndex + j + 1; + *index++ = lineIndex + j; + } + } + + 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(); + } + glPushMatrix(); + glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z); + glm::vec3 axis = glm::axis(heightfield->getRotation()); + glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z); + glScalef(heightfield->getScale(), heightfield->getScale() * heightfield->getAspectY(), + heightfield->getScale() * heightfield->getAspectZ()); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / innerWidth, 1.0f / innerHeight, + 2.0f / (innerWidth * innerHeight)); + if (heightfield->getColor()) { + int colorWidth = heightfield->getColor()->getWidth(); + int colorHeight = heightfield->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)innerWidth / colorWidth, + (float)innerHeight / colorHeight); + } + + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + glBindTexture(GL_TEXTURE_2D, 0); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); + + glDisable(GL_ALPHA_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true); + + glPopMatrix(); + + bufferPair.first.release(); + bufferPair.second.release(); } void HeightfieldRenderer::applyHeight(const HeightfieldHeightPointer& height) { @@ -3075,3 +3202,6 @@ void HeightfieldRenderer::applyMaterial(const HeightfieldMaterialPointer& materi } glBindTexture(GL_TEXTURE_2D, 0); } + +QHash HeightfieldRenderer::_bufferPairs; + diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 8986bc109a..f194e3d0f9 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -475,8 +475,9 @@ private: GLuint _materialTextureID; QVector _networkTextures; + typedef QPair IntPair; typedef QPair BufferPair; - static QHash _bufferPairs; + static QHash _bufferPairs; }; #endif // hifi_MetavoxelSystem_h diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 2b3b0a3870..63ae1d33ac 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -135,6 +135,8 @@ MetavoxelEditor::MetavoxelEditor() : connect(Application::getInstance(), SIGNAL(simulating(float)), SLOT(simulate(float))); connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); + connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering, + this, &MetavoxelEditor::renderPreview); Application::getInstance()->getGLWidget()->installEventFilter(this); @@ -369,6 +371,13 @@ void MetavoxelEditor::render() { glDepthMask(GL_TRUE); } +void MetavoxelEditor::renderPreview() { + MetavoxelTool* tool = getActiveTool(); + if (tool) { + tool->renderPreview(); + } +} + void MetavoxelEditor::addTool(MetavoxelTool* tool) { _tools.append(tool); layout()->addWidget(tool); @@ -406,6 +415,10 @@ void MetavoxelTool::render() { // nothing by default } +void MetavoxelTool::renderPreview() { + // nothing by default +} + BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) : MetavoxelTool(editor, name, usesValue, userFacing) { @@ -586,6 +599,15 @@ void GlobalSetTool::apply() { PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText, bool usesValue) : MetavoxelTool(editor, name, usesValue) { + QWidget* widget = new QWidget(this); + layout()->addWidget(widget); + QHBoxLayout* box = new QHBoxLayout(); + widget->setLayout(box); + box->setContentsMargins(QMargins()); + box->addStretch(1); + box->addWidget(_followMouse = new QCheckBox("Follow Mouse")); + box->addStretch(1); + if (!placeText.isEmpty()) { QPushButton* button = new QPushButton(placeText); layout()->addWidget(button); @@ -594,12 +616,12 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, } void PlaceSpannerTool::simulate(float deltaTime) { - if (Application::getInstance()->isMouseHidden()) { + if (!Application::getInstance()->getGLWidget()->hasFocus() || Application::getInstance()->isMouseHidden()) { return; } Spanner* spanner = static_cast(getSpanner(true).data()); Transformable* transformable = qobject_cast(spanner); - if (transformable) { + if (transformable && _followMouse->isChecked()) { // find the intersection of the mouse ray with the grid and place the transformable there glm::quat rotation = _editor->getGridRotation(); glm::quat inverseRotation = glm::inverse(rotation); @@ -613,14 +635,10 @@ void PlaceSpannerTool::simulate(float deltaTime) { spanner->getRenderer()->simulate(deltaTime); } -void PlaceSpannerTool::render() { - if (Application::getInstance()->isMouseHidden()) { - return; - } +void PlaceSpannerTool::renderPreview() { Spanner* spanner = static_cast(getSpanner().data()); - const float SPANNER_ALPHA = 0.25f; QColor color = getColor(); - spanner->getRenderer()->render(glm::vec4(color.redF(), color.greenF(), color.blueF(), SPANNER_ALPHA), + spanner->getRenderer()->render(glm::vec4(color.redF(), color.greenF(), color.blueF(), 1.0f), SpannerRenderer::DEFAULT_MODE); } @@ -782,9 +800,10 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : _form->addRow("Color:", _color = new QPushButton()); connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); - - connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering, - this, &ImportHeightfieldTool::renderPreview); +} + +void ImportHeightfieldTool::renderPreview() { + _preview.render(_translation->getValue(), _translation->getSingleStep()); } void ImportHeightfieldTool::apply() { @@ -961,12 +980,6 @@ void ImportHeightfieldTool::updatePreview() { _preview.setBuffers(buffers); } -void ImportHeightfieldTool::renderPreview() { - if (isVisible()) { - _preview.render(_translation->getValue(), _translation->getSingleStep()); - } -} - EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) : HeightfieldTool(editor, "Erase Heightfield") { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 5607439807..f0268fbe7d 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -62,6 +62,7 @@ private slots: void simulate(float deltaTime); void render(); + void renderPreview(); private: @@ -104,6 +105,9 @@ public: /// Renders the tool's interface, if any. virtual void render(); + /// Renders the tool's metavoxel preview, if any. + virtual void renderPreview(); + protected: MetavoxelEditor* _editor; @@ -184,7 +188,7 @@ public: virtual void simulate(float deltaTime); - virtual void render(); + virtual void renderPreview(); virtual bool appliesTo(const AttributePointer& attribute) const; @@ -199,6 +203,10 @@ protected: protected slots: void place(); + +private: + + QCheckBox* _followMouse; }; /// Allows inserting a spanner into the scene. @@ -273,6 +281,8 @@ public: ImportHeightfieldTool(MetavoxelEditor* editor); + virtual void renderPreview(); + protected: virtual void apply(); @@ -283,7 +293,6 @@ private slots: void selectColorFile(); void updateHeightImage(); void updatePreview(); - void renderPreview(); private: diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 50d5e31f56..1b0dd804c7 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -687,11 +687,6 @@ void HeightfieldHeightEditor::select() { return; } settings.setValue("heightDir", QFileInfo(result).path()); - QImage image; - if (!image.load(result)) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } const quint16 CONVERSION_OFFSET = 1; if (result.toLower().endsWith(".raw")) { QFile input(result); @@ -723,6 +718,11 @@ void HeightfieldHeightEditor::select() { _clear->setEnabled(true); return; } + QImage image; + if (!image.load(result)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } image = image.convertToFormat(QImage::Format_RGB888); int width = getHeightfieldSize(image.width()) + 2 * HeightfieldHeight::HEIGHT_BORDER; int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER; @@ -949,5 +949,5 @@ QByteArray Heightfield::getRendererClassName() const { void Heightfield::updateBounds() { glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ); glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); - setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent)); + setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(glm::vec3(), extent)); } From 16bf2a3ad36b8317b9a4041985ead1b6489f6c2c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Nov 2014 15:37:15 -0800 Subject: [PATCH 08/38] More chipping away at heightfield spanners. --- .../metavoxels/src/AttributeRegistry.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 207 +++++++++++++++++- libraries/metavoxels/src/Spanner.h | 6 + 3 files changed, 208 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index ddc23980b5..6d11f5e0d3 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -329,7 +329,7 @@ static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = fa } } -const QImage decodeHeightfieldImage(const QByteArray& data) { +static QImage decodeHeightfieldImage(const QByteArray& data) { switch (data.at(0)) { case NULL_HEIGHTFIELD_IMAGE: default: diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 1b0dd804c7..f6bdbc0be1 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -609,9 +610,19 @@ HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents } HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) { + read(in, bytes); } HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); + _contents = reference->getContents(); } void HeightfieldHeight::write(Bitstream& out) { @@ -624,6 +635,19 @@ void HeightfieldHeight::write(Bitstream& out) { } void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) { + if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void HeightfieldHeight::read(Bitstream& in, int bytes) { } Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) { @@ -641,15 +665,27 @@ Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value) { if (size == 0) { value = HeightfieldHeightPointer(); } else { - value = new HeightfieldHeight(0, QVector()); + value = new HeightfieldHeight(in, size); } return in; } template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } } template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldHeightPointer(); + } else { + value = new HeightfieldHeight(*this, size, reference); + } } HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) : @@ -745,27 +781,95 @@ void HeightfieldHeightEditor::clear() { _clear->setEnabled(false); } +enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; + +static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = false) { + if (image.isNull()) { + return QByteArray(1, NULL_HEIGHTFIELD_IMAGE); + } + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + const int JPEG_ENCODE_THRESHOLD = 16; + if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD && !lossless) { + qint32 offsetX = image.offset().x(), offsetY = image.offset().y(); + buffer.write((char*)&offsetX, sizeof(qint32)); + buffer.write((char*)&offsetY, sizeof(qint32)); + image.save(&buffer, "JPG"); + return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data()); + + } else { + buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE); + image.save(&buffer, "PNG"); + return buffer.data(); + } +} + +static QImage decodeHeightfieldImage(const QByteArray& data) { + switch (data.at(0)) { + case NULL_HEIGHTFIELD_IMAGE: + default: + return QImage(); + + case NORMAL_HEIGHTFIELD_IMAGE: + return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1)); + + case DEFLATED_HEIGHTFIELD_IMAGE: { + QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1); + const int OFFSET_SIZE = sizeof(qint32) * 2; + QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE); + const qint32* offsets = (const qint32*)inflated.constData(); + image.setOffset(QPoint(offsets[0], offsets[1])); + return image; + } + } +} + HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : HeightfieldData(width), _contents(contents) { } HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) { + read(in, bytes); } HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); + _contents = reference->getContents(); } void HeightfieldColor::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - + QImage image; + image = QImage((uchar*)_contents.data(), _width, _contents.size() / (_width * COLOR_BYTES), QImage::Format_RGB888); + _encoded = encodeHeightfieldImage(image); } out << _encoded.size(); out.writeAligned(_encoded); } void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) { + if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void HeightfieldColor::read(Bitstream& in, int bytes) { } Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) { @@ -783,16 +887,27 @@ Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value) { if (size == 0) { value = HeightfieldColorPointer(); } else { - value = new HeightfieldColor(0, QByteArray()); + value = new HeightfieldColor(in, size); } return in; } template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { - + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } } template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldColorPointer(); + } else { + value = new HeightfieldColor(*this, size, reference); + } } HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) : @@ -847,6 +962,29 @@ void HeightfieldColorEditor::clear() { _clear->setEnabled(false); } +const int HEIGHTFIELD_MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; + +static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(HEIGHTFIELD_MATERIAL_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + inflated.append(contents); + return qCompress(inflated); +} + +static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); +} + HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, const QVector& materials) : HeightfieldData(width), @@ -855,15 +993,43 @@ HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, } HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) { + read(in, bytes); } HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + in.readDelta(_materials, reference->getMaterials()); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); + _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const char* src = delta.constData(); + char* dest = _contents.data() + minY * _width + minX; + for (int y = 0; y < height; y++, src += width, dest += _width) { + memcpy(dest, src, width); + } } void HeightfieldMaterial::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - + _encoded = encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents); } out << _encoded.size(); out.writeAligned(_encoded); @@ -871,6 +1037,23 @@ void HeightfieldMaterial::write(Bitstream& out) { } void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) { + if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); + out.writeDelta(_materials, reference->getMaterials()); +} + +void HeightfieldMaterial::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); + in >> _materials; } Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value) { @@ -888,16 +1071,28 @@ Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value) { if (size == 0) { value = HeightfieldMaterialPointer(); } else { - value = new HeightfieldMaterial(0, QByteArray(), QVector()); + value = new HeightfieldMaterial(in, size); } return in; } template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) { + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } } template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldMaterialPointer(); + } else { + value = new HeightfieldMaterial(*this, size, reference); + } } Heightfield::Heightfield() : diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 44f400887a..d9859d0947 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -334,6 +334,8 @@ public: private: + void read(Bitstream& in, int bytes); + QVector _contents; }; @@ -392,6 +394,8 @@ public: private: + void read(Bitstream& in, int bytes); + QByteArray _contents; }; @@ -451,6 +455,8 @@ public: private: + void read(Bitstream& in, int bytes); + QByteArray _contents; QVector _materials; }; From d05bb49a7417fb2af7cdca88a6838c3eaffbaab2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Nov 2014 15:40:54 -0800 Subject: [PATCH 09/38] Yet more heightfield spanner bits. --- .../shaders/metavoxel_heightfield_base.vert | 8 +- .../shaders/metavoxel_heightfield_splat.vert | 6 +- interface/src/MetavoxelSystem.cpp | 117 +++++-- interface/src/ui/MetavoxelEditor.cpp | 294 +++--------------- interface/src/ui/MetavoxelEditor.h | 43 +-- .../metavoxels/src/AttributeRegistry.cpp | 1 - libraries/metavoxels/src/Spanner.cpp | 33 +- libraries/metavoxels/src/Spanner.h | 4 + 8 files changed, 186 insertions(+), 320 deletions(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_base.vert b/interface/resources/shaders/metavoxel_heightfield_base.vert index 55bf758636..03a99bd57c 100644 --- a/interface/resources/shaders/metavoxel_heightfield_base.vert +++ b/interface/resources/shaders/metavoxel_heightfield_base.vert @@ -15,7 +15,7 @@ uniform sampler2D heightMap; // the distance between height points in texture space -uniform vec3 heightScale; +uniform vec4 heightScale; // the scale between height and color textures uniform vec2 colorScale; @@ -31,9 +31,9 @@ void main(void) { texture2D(heightMap, heightCoord - vec2(0.0, heightScale.t)).r, texture2D(heightMap, heightCoord + vec2(0.0, heightScale.t)).r); vec4 neighborsZero = step(1.0 / 65535.0, neighborHeights); - normal = normalize(gl_ModelViewMatrix * vec4( - heightScale.s * (neighborHeights.x - neighborHeights.y) * neighborsZero.x * neighborsZero.y, heightScale.p, - heightScale.t * (neighborHeights.z - neighborHeights.w) * neighborsZero.z * neighborsZero.w, 0.0)); + normal = vec4(normalize(gl_NormalMatrix * vec3( + heightScale.p * (neighborHeights.y - neighborHeights.x) * neighborsZero.x * neighborsZero.y, 1.0, + heightScale.q * (neighborHeights.w - neighborHeights.z) * neighborsZero.z * neighborsZero.w)), 0.0); // add the height to the position float height = texture2D(heightMap, heightCoord).r; diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.vert b/interface/resources/shaders/metavoxel_heightfield_splat.vert index 926bcdd6c3..d54da347cc 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.vert +++ b/interface/resources/shaders/metavoxel_heightfield_splat.vert @@ -18,10 +18,10 @@ uniform sampler2D heightMap; uniform sampler2D textureMap; // the distance between height points in texture space -uniform float heightScale; +uniform vec2 heightScale; // the scale between height and texture textures -uniform float textureScale; +uniform vec2 textureScale; // the splat texture offset uniform vec2 splatTextureOffset; @@ -58,7 +58,7 @@ void main(void) { gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0); // compute the alpha values for each texture - float value = texture2D(textureMap, (gl_MultiTexCoord0.st - vec2(heightScale, heightScale)) * textureScale).r; + float value = texture2D(textureMap, (gl_MultiTexCoord0.st - heightScale) * textureScale).r; vec4 valueVector = vec4(value, value, value, value); alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima); } diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 3d3f3de9f5..36f6a690af 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2868,7 +2868,7 @@ void SphereRenderer::render(const glm::vec4& color, Mode mode) { glm::vec3 axis = glm::axis(rotation); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - Application::getInstance()->getGeometryCache()->renderSphere(sphere->getScale(), 10, 10); + Application::getInstance()->getDeferredLightingEffect()->renderSolidSphere(sphere->getScale(), 32, 32); glPopMatrix(); } @@ -2889,7 +2889,7 @@ void CuboidRenderer::render(const glm::vec4& color, Mode mode) { glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glScalef(1.0f, cuboid->getAspectY(), cuboid->getAspectZ()); - glutSolidCube(cuboid->getScale() * 2.0f); + Application::getInstance()->getDeferredLightingEffect()->renderSolidCube(cuboid->getScale() * 2.0f); glPopMatrix(); } @@ -3038,12 +3038,12 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { float xStep = 1.0f / (innerWidth - 1); float zStep = 1.0f / (innerHeight - 1); float z = -zStep; - float sStep = 1.0f / innerWidth; - float tStep = 1.0f / innerHeight; - float t = -tStep / 2.0f; + float sStep = 1.0f / width; + float tStep = 1.0f / height; + float t = tStep / 2.0f; for (int i = 0; i < height; i++, z += zStep, t += tStep) { float x = -xStep; - float s = -sStep / 2.0f; + float s = sStep / 2.0f; const float SKIRT_LENGTH = 0.25f; float baseY = (i == 0 || i == height - 1) ? -SKIRT_LENGTH : 0.0f; for (int j = 0; j < width; j++, point++, x += xStep, s += sStep) { @@ -3086,8 +3086,8 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z); glm::vec3 axis = glm::axis(heightfield->getRotation()); glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z); - glScalef(heightfield->getScale(), heightfield->getScale() * heightfield->getAspectY(), - heightfield->getScale() * heightfield->getAspectZ()); + float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ(); + glScalef(xScale, xScale * heightfield->getAspectY(), zScale); Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); @@ -3106,17 +3106,16 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + float sDistance = 2.0f / (innerWidth - 1); + float tDistance = 2.0f / (innerHeight - 1); + float distanceProduct = -sDistance * tDistance; DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / innerWidth, 1.0f / innerHeight, - 2.0f / (innerWidth * innerHeight)); - if (heightfield->getColor()) { - int colorWidth = heightfield->getColor()->getWidth(); - int colorHeight = heightfield->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)innerWidth / colorWidth, - (float)innerHeight / colorHeight); - } - + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / width, 1.0f / height, + sDistance / distanceProduct, tDistance / distanceProduct); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)width / innerWidth, + (float)height / innerHeight); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); glActiveTexture(GL_TEXTURE1); @@ -3127,19 +3126,93 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); glEnable(GL_BLEND); + if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); + const DefaultMetavoxelRendererImplementation::SplatLocations& locations = + DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.heightScale, 1.0f / width, 1.0f / height); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureScale, (float)width / innerWidth, (float)height / innerHeight); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(locations.splatTextureOffset, + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); + + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + + const QVector& materials = heightfield->getMaterial()->getMaterials(); + for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { + QVector4D scalesS, scalesT; + + for (int j = 0; j < SPLAT_COUNT; j++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); + int index = i + j; + if (index < _networkTextures.size()) { + const NetworkTexturePointer& texture = _networkTextures.at(index); + if (texture) { + MaterialObject* material = static_cast(materials.at(index).data()); + scalesS[j] = xScale / material->getScaleS(); + scalesT[j] = zScale / material->getScaleT(); + glBindTexture(GL_TEXTURE_2D, texture->getID()); + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } + } + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.splatTextureScalesS, scalesS); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.splatTextureScalesT, scalesT); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureValueMinima, + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureValueMaxima, + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + } + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthMask(true); + glDepthFunc(GL_LESS); + } + + glBindTexture(GL_TEXTURE_2D, 0); + + glDisable(GL_CULL_FACE); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true); - glPopMatrix(); bufferPair.first.release(); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 63ae1d33ac..4bf1170f87 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -125,7 +125,6 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldMaterialBrushTool(this)); addTool(new ImportHeightfieldTool(this)); - addTool(new EraseHeightfieldTool(this)); addTool(new VoxelMaterialBoxTool(this)); addTool(new VoxelMaterialSpannerTool(this)); addTool(new VoxelMaterialBrushTool(this)); @@ -606,6 +605,7 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, box->setContentsMargins(QMargins()); box->addStretch(1); box->addWidget(_followMouse = new QCheckBox("Follow Mouse")); + _followMouse->setChecked(true); box->addStretch(1); if (!placeText.isEmpty()) { @@ -616,12 +616,9 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, } void PlaceSpannerTool::simulate(float deltaTime) { - if (!Application::getInstance()->getGLWidget()->hasFocus() || Application::getInstance()->isMouseHidden()) { - return; - } Spanner* spanner = static_cast(getSpanner(true).data()); Transformable* transformable = qobject_cast(spanner); - if (transformable && _followMouse->isChecked()) { + if (transformable && _followMouse->isChecked() && !Application::getInstance()->isMouseHidden()) { // find the intersection of the mouse ray with the grid and place the transformable there glm::quat rotation = _editor->getGridRotation(); glm::quat inverseRotation = glm::inverse(rotation); @@ -741,7 +738,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : _scale->setMinimum(-FLT_MAX); _scale->setMaximum(FLT_MAX); _scale->setPrefix("2^"); - _scale->setValue(3.0); + _scale->setValue(2.0); QPushButton* applyButton = new QPushButton("Apply"); layout()->addWidget(applyButton); @@ -749,7 +746,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : } bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("HeightfieldAttribute"); + return attribute->inherits("SpannerSetAttribute"); } void HeightfieldTool::render() { @@ -761,269 +758,62 @@ void HeightfieldTool::render() { ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : HeightfieldTool(editor, "Import Heightfield"), - _loadingImage(false) { + _spanner(new Heightfield()) { - _form->addRow("Block Size:", _blockSize = new QSpinBox()); - _blockSize->setPrefix("2^"); - _blockSize->setMinimum(1); - _blockSize->setValue(5); - - connect(_blockSize, static_cast(&QSpinBox::valueChanged), this, - &ImportHeightfieldTool::updatePreview); - _form->addRow("Height:", _height = new QPushButton()); - connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); - - _form->addRow(_rawOptions = new QWidget()); - QHBoxLayout* rawLayout = new QHBoxLayout(); - _rawOptions->setLayout(rawLayout); - _rawOptions->setVisible(false); - - rawLayout->addStretch(1); - rawLayout->addWidget(new QLabel("Scale:")); - rawLayout->addWidget(_heightScale = new QDoubleSpinBox()); + _form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox()); const double MAX_OFFSET_SCALE = 100000.0; _heightScale->setMaximum(MAX_OFFSET_SCALE); - _heightScale->setSingleStep(0.0001); - _heightScale->setDecimals(4); - _heightScale->setValue(1.0); + _heightScale->setDecimals(3); + _heightScale->setSingleStep(0.001); + _heightScale->setValue(0.05); connect(_heightScale, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateHeightImage); - - rawLayout->addSpacing(15); - rawLayout->addWidget(new QLabel("Offset:")); - rawLayout->addWidget(_heightOffset = new QDoubleSpinBox()); + &ImportHeightfieldTool::updateSpanner); + + _form->addRow("Height Offset:", _heightOffset = new QDoubleSpinBox()); _heightOffset->setMinimum(-MAX_OFFSET_SCALE); _heightOffset->setMaximum(MAX_OFFSET_SCALE); - _heightOffset->setDecimals(4); + _heightOffset->setSingleStep(0.01); connect(_heightOffset, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateHeightImage); + &ImportHeightfieldTool::updateSpanner); - _form->addRow("Color:", _color = new QPushButton()); - connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); + _form->addRow("Height:", _height = new HeightfieldHeightEditor(this)); + connect(_height, &HeightfieldHeightEditor::heightChanged, this, &ImportHeightfieldTool::updateSpanner); + + _form->addRow("Color:", _color = new HeightfieldColorEditor(this)); + connect(_color, &HeightfieldColorEditor::colorChanged, this, &ImportHeightfieldTool::updateSpanner); + + connect(_translation, &Vec3Editor::valueChanged, this, &ImportHeightfieldTool::updateSpanner); + connect(_scale, static_cast(&QDoubleSpinBox::valueChanged), this, + &ImportHeightfieldTool::updateSpanner); +} + +void ImportHeightfieldTool::simulate(float deltaTime) { + static_cast(_spanner.data())->getRenderer()->simulate(deltaTime); } void ImportHeightfieldTool::renderPreview() { - _preview.render(_translation->getValue(), _translation->getSingleStep()); + static_cast(_spanner.data())->getRenderer()->render(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), + SpannerRenderer::DEFAULT_MODE); } void ImportHeightfieldTool::apply() { - float scale = _translation->getSingleStep(); - foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) { - HeightfieldBuffer* buffer = static_cast(bufferData.data()); - MetavoxelData data; - data.setSize(scale); - - QByteArray height = buffer->getUnextendedHeight(); - HeightfieldHeightDataPointer heightPointer(new HeightfieldHeightData(height)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); - - MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( - _translation->getValue() + buffer->getTranslation() * scale, data)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - - int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) / - (buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION); - float colorScale = scale / colorUnits; - - for (int y = 0; y < colorUnits; y++) { - for (int x = 0; x < colorUnits; x++) { - MetavoxelData data; - data.setSize(colorScale); - - QByteArray color; - if (buffer->getColor().isEmpty()) { - const unsigned char WHITE_VALUE = 0xFF; - color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); - } else { - color = buffer->getUnextendedColor(x, y); - } - HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode( - AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), - encodeInline(colorPointer)))); - - QByteArray material(height.size(), 0); - HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode( - AttributeValue(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), - encodeInline(materialPointer)))); - - MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( - _translation->getValue() + buffer->getTranslation() * scale + glm::vec3(x, 0.0f, y) * colorScale, data)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - } - } - } } -const float EIGHT_BIT_MAXIMUM = 255.0f; +const int HEIGHTFIELD_BLOCK_SIZE = 32; -void ImportHeightfieldTool::selectHeightFile() { - QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(), - "Images (*.png *.jpg *.bmp *.raw)"); - if (filename.isNull()) { - return; - } - if (filename.toLower().endsWith(".raw")) { - QFile input(filename); - input.open(QIODevice::ReadOnly); - QDataStream in(&input); - in.setByteOrder(QDataStream::LittleEndian); - _rawHeight.clear(); - int minHeight = numeric_limits::max(); - int maxHeight = numeric_limits::min(); - while (!in.atEnd()) { - quint16 height; - in >> height; - _rawHeight.append(height); - minHeight = qMin(minHeight, (int)height); - maxHeight = qMax(maxHeight, (int)height); - } - _height->setText(filename); - _rawOptions->setVisible(true); - _loadingImage = true; - _heightScale->setValue((EIGHT_BIT_MAXIMUM - 1.0f) / (maxHeight - minHeight)); - _heightOffset->setValue(-minHeight * _heightScale->value() + 1.0); - _loadingImage = false; - updateHeightImage(); - return; - } - if (!_heightImage.load(filename)) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } - _rawOptions->setVisible(false); - _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 *.bmp)"); - 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(); -} - -void ImportHeightfieldTool::updateHeightImage() { - if (_loadingImage) { - return; - } - int size = glm::sqrt(float(_rawHeight.size())); - _heightImage = QImage(size, size, QImage::Format_RGB888); - const quint16* src = _rawHeight.constData(); - float scale = _heightScale->value(), offset = _heightOffset->value(); - for (int y = 0; y < size; y++) { - uchar* dest = _heightImage.scanLine(y); - for (const quint16* end = src + size; src != end; src++) { - uchar height = glm::clamp(*src * scale + offset, 1.0f, EIGHT_BIT_MAXIMUM); - *dest++ = height; - *dest++ = height; - *dest++ = height; - } - } - updatePreview(); -} - -void ImportHeightfieldTool::updatePreview() { - QVector buffers; - if (_heightImage.width() > 0 && _heightImage.height() > 0) { - float z = 0.0f; - int blockSize = pow(2.0, _blockSize->value()); - int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f)); - int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0)); - int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE; - for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { - float x = 0.0f; - for (int j = 0; j < _heightImage.width(); j += blockSize, x++) { - QByteArray height(heightSize * heightSize, 0); - int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0); - int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0); - int offsetY = extendedI - i + HeightfieldBuffer::HEIGHT_BORDER; - int offsetX = extendedJ - j + HeightfieldBuffer::HEIGHT_BORDER; - int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI); - int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ); - for (int y = 0; y < rows; y++) { - uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES; - char* dest = height.data() + (y + offsetY) * heightSize + offsetX; - for (int x = 0; x < columns; x++) { - *dest++ = qMax((uchar)1, *src); - src += DataBlock::COLOR_BYTES; - } - } - QByteArray color; - if (!_colorImage.isNull()) { - int colorI = (i / blockSize) * colorBlockSize; - int colorJ = (j / blockSize) * colorBlockSize; - color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0); - rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI)); - columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ)); - for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES, - _colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES, - columns * DataBlock::COLOR_BYTES); - } - } - buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); - } - } - } - _preview.setBuffers(buffers); -} - -EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) : - HeightfieldTool(editor, "Erase Heightfield") { +void ImportHeightfieldTool::updateSpanner() { + Heightfield* heightfield = static_cast(_spanner.data()); + heightfield->setHeight(_height->getHeight()); + heightfield->setColor(_color->getColor()); - _form->addRow("Width:", _width = new QSpinBox()); - _width->setMinimum(1); - _width->setMaximum(INT_MAX); - _form->addRow("Length:", _length = new QSpinBox()); - _length->setMinimum(1); - _length->setMaximum(INT_MAX); -} - -void EraseHeightfieldTool::render() { - HeightfieldTool::render(); - - glColor3f(1.0f, 0.0f, 0.0f); - glLineWidth(4.0f); - - glPushMatrix(); - glm::vec3 translation = _translation->getValue(); - glTranslatef(translation.x, translation.y, translation.z); - float scale = _translation->getSingleStep(); - glScalef(scale * _width->value(), scale, scale * _length->value()); - glTranslatef(0.5f, 0.5f, 0.5f); - - glutWireCube(1.0); - - glPopMatrix(); - - glLineWidth(1.0f); -} - -void EraseHeightfieldTool::apply() { - // clear the heightfield - float scale = _translation->getSingleStep(); - BoxSetEdit edit(Box(_translation->getValue(), _translation->getValue() + - glm::vec3(_width->value() * scale, scale, _length->value() * scale)), scale, - OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldAttribute())); - MetavoxelEditMessage message = { QVariant::fromValue(edit) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - - // and the color - edit.value = OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute()); - message.edit = QVariant::fromValue(edit); - Application::getInstance()->getMetavoxels()->applyEdit(message, true); + float scale = pow(2.0, _scale->value()); + if (_height->getHeight()) { + int innerSize = _height->getHeight()->getWidth() - HeightfieldHeight::HEIGHT_EXTENSION; + scale *= glm::ceil((float)innerSize / HEIGHTFIELD_BLOCK_SIZE); + } + heightfield->setScale(scale); + heightfield->setAspectY(_heightScale->value()); + heightfield->setTranslation(_translation->getValue() + glm::vec3(0.0f, _heightOffset->value(), 0.0f)); } HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) : diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index f0268fbe7d..cccb41ecfc 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -281,6 +281,8 @@ public: ImportHeightfieldTool(MetavoxelEditor* editor); + virtual void simulate(float deltaTime); + virtual void renderPreview(); protected: @@ -289,48 +291,17 @@ protected: private slots: - void selectHeightFile(); - void selectColorFile(); - void updateHeightImage(); - void updatePreview(); - + void updateSpanner(); + private: - QSpinBox* _blockSize; - - QPushButton* _height; - QWidget* _rawOptions; QDoubleSpinBox* _heightScale; QDoubleSpinBox* _heightOffset; - bool _loadingImage; - QPushButton* _color; + HeightfieldHeightEditor* _height; + HeightfieldColorEditor* _color; - QVector _rawHeight; - QImage _heightImage; - QImage _colorImage; - - HeightfieldPreview _preview; -}; - -/// Allows clearing heighfield blocks. -class EraseHeightfieldTool : public HeightfieldTool { - Q_OBJECT - -public: - - EraseHeightfieldTool(MetavoxelEditor* editor); - - virtual void render(); - -protected: - - virtual void apply(); - -private: - - QSpinBox* _width; - QSpinBox* _length; + SharedObjectPointer _spanner; }; /// Base class for tools that allow painting on heightfields. diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 6d11f5e0d3..b90050f598 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -59,7 +59,6 @@ AttributeRegistry::AttributeRegistry() : const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - _heightfieldAttribute->setUserFacing(true); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f6bdbc0be1..908226367e 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -29,10 +29,10 @@ using namespace std; REGISTER_META_OBJECT(Spanner) +REGISTER_META_OBJECT(Heightfield) REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(Cuboid) REGISTER_META_OBJECT(StaticModel) -REGISTER_META_OBJECT(Heightfield) static int heightfieldHeightTypeId = registerSimpleMetaType(); static int heightfieldColorTypeId = registerSimpleMetaType(); @@ -604,6 +604,33 @@ HeightfieldData::HeightfieldData(int width) : _width(width) { } +const int HEIGHTFIELD_HEIGHT_HEADER_SIZE = sizeof(qint32) * 4; + +static QByteArray encodeHeightfieldHeight(int offsetX, int offsetY, int width, int height, const QVector& contents) { + QByteArray inflated(HEIGHTFIELD_HEIGHT_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + inflated.append((const char*)contents.constData(), contents.size() * sizeof(quint16)); + return qCompress(inflated); +} + +static QVector decodeHeightfieldHeight(const QByteArray& encoded, int& offsetX, int& offsetY, + int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + int payloadSize = inflated.size() - HEIGHTFIELD_HEIGHT_HEADER_SIZE; + QVector decoded(payloadSize / sizeof(quint16)); + memcpy(decoded.data(), inflated.constData() + HEIGHTFIELD_HEIGHT_HEADER_SIZE, payloadSize); + return decoded; +} + HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents) : HeightfieldData(width), _contents(contents) { @@ -628,7 +655,7 @@ HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const Heightfield void HeightfieldHeight::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - + _encoded = encodeHeightfieldHeight(0, 0, _width, _contents.size() / _width, _contents); } out << _encoded.size(); out.writeAligned(_encoded); @@ -648,6 +675,8 @@ void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointe } void HeightfieldHeight::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldHeight(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); } Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index d9859d0947..6fdf93a85a 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -356,6 +356,8 @@ public: HeightfieldHeightEditor(QWidget* parent = NULL); + const HeightfieldHeightPointer& getHeight() const { return _height; } + signals: void heightChanged(const HeightfieldHeightPointer& height); @@ -416,6 +418,8 @@ public: HeightfieldColorEditor(QWidget* parent = NULL); + const HeightfieldColorPointer& getColor() const { return _color; } + signals: void colorChanged(const HeightfieldColorPointer& color); From 12f5bccd81029248b32568289775c3a00abdb421 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Nov 2014 18:03:42 -0800 Subject: [PATCH 10/38] More chipping away. --- interface/src/ui/MetavoxelEditor.cpp | 88 +++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 4bf1170f87..2cbd445f80 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -763,9 +763,8 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : _form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox()); const double MAX_OFFSET_SCALE = 100000.0; _heightScale->setMaximum(MAX_OFFSET_SCALE); - _heightScale->setDecimals(3); - _heightScale->setSingleStep(0.001); - _heightScale->setValue(0.05); + _heightScale->setSingleStep(0.01); + _heightScale->setValue(8.0); connect(_heightScale, static_cast(&QDoubleSpinBox::valueChanged), this, &ImportHeightfieldTool::updateSpanner); @@ -796,23 +795,94 @@ void ImportHeightfieldTool::renderPreview() { SpannerRenderer::DEFAULT_MODE); } -void ImportHeightfieldTool::apply() { -} - const int HEIGHTFIELD_BLOCK_SIZE = 32; +void ImportHeightfieldTool::apply() { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); + if (!(_height->getHeight() && attribute)) { + return; + } + int width = _height->getHeight()->getWidth(); + const QVector& contents = _height->getHeight()->getContents(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + float scale = pow(2.0, _scale->value()); + + for (int i = 0; i < innerHeight; i += HEIGHTFIELD_BLOCK_SIZE) { + for (int j = 0; j < innerWidth; j += HEIGHTFIELD_BLOCK_SIZE) { + Heightfield* heightfield = new Heightfield(); + + int extendedHeightSize = HEIGHTFIELD_BLOCK_SIZE + HeightfieldHeight::HEIGHT_EXTENSION; + QVector heightContents(extendedHeightSize * extendedHeightSize); + quint16* dest = heightContents.data(); + const quint16* src = contents.constData() + i * width + j; + int copyWidth = qMin(width - j, extendedHeightSize); + int copyHeight = qMin(height - i, extendedHeightSize); + for (int z = 0; z < copyHeight; z++, src += width, dest += extendedHeightSize) { + memcpy(dest, src, copyWidth * sizeof(quint16)); + } + heightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(extendedHeightSize, heightContents))); + + int materialWidth = HEIGHTFIELD_BLOCK_SIZE + HeightfieldData::SHARED_EDGE; + int materialHeight = materialWidth; + if (_color->getColor()) { + int colorWidth = _color->getColor()->getWidth(); + const QByteArray& contents = _color->getColor()->getContents(); + int colorHeight = contents.size() / (colorWidth * DataBlock::COLOR_BYTES); + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + materialWidth = HEIGHTFIELD_BLOCK_SIZE * innerColorWidth / innerWidth + HeightfieldData::SHARED_EDGE; + materialHeight = HEIGHTFIELD_BLOCK_SIZE * innerColorHeight / innerHeight + HeightfieldData::SHARED_EDGE; + QByteArray colorContents(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0); + int colorI = i * (materialWidth - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE; + int colorJ = j * (materialHeight - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE; + char* dest = colorContents.data(); + const char* src = contents.constData() + (colorI * colorWidth + colorJ) * DataBlock::COLOR_BYTES; + int copyWidth = qMin(colorWidth - colorJ, materialWidth); + int copyHeight = qMin(colorHeight - colorI, materialHeight); + for (int z = 0; z < copyHeight; z++, src += colorWidth * DataBlock::COLOR_BYTES, + dest += materialWidth * DataBlock::COLOR_BYTES) { + memcpy(dest, src, copyWidth * DataBlock::COLOR_BYTES); + } + heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, colorContents))); + + } else { + heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, + QByteArray(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0xFF)))); + } + heightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, + QByteArray(materialWidth * materialHeight, 0), QVector()))); + + heightfield->setScale(scale); + heightfield->setAspectY(_heightScale->value() / scale); + heightfield->setTranslation(_translation->getValue() + glm::vec3((j / HEIGHTFIELD_BLOCK_SIZE) * scale, + _heightOffset->value(), (i / HEIGHTFIELD_BLOCK_SIZE) * scale)); + + MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, heightfield)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message); + } + } +} + void ImportHeightfieldTool::updateSpanner() { Heightfield* heightfield = static_cast(_spanner.data()); heightfield->setHeight(_height->getHeight()); heightfield->setColor(_color->getColor()); float scale = pow(2.0, _scale->value()); + float aspectZ = 1.0f; if (_height->getHeight()) { - int innerSize = _height->getHeight()->getWidth() - HeightfieldHeight::HEIGHT_EXTENSION; - scale *= glm::ceil((float)innerSize / HEIGHTFIELD_BLOCK_SIZE); + int width = _height->getHeight()->getWidth(); + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = _height->getHeight()->getContents().size() / width - HeightfieldHeight::HEIGHT_EXTENSION; + float widthBlocks = glm::ceil((float)innerWidth / HEIGHTFIELD_BLOCK_SIZE); + scale *= widthBlocks; + aspectZ = glm::ceil((float)innerHeight / HEIGHTFIELD_BLOCK_SIZE) / widthBlocks; } heightfield->setScale(scale); - heightfield->setAspectY(_heightScale->value()); + heightfield->setAspectY(_heightScale->value() / scale); + heightfield->setAspectZ(aspectZ); heightfield->setTranslation(_translation->getValue() + glm::vec3(0.0f, _heightOffset->value(), 0.0f)); } From 110cf93cc42356ae59263740a1c6a99249a8a0e4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Nov 2014 19:27:25 -0800 Subject: [PATCH 11/38] Basic encoding/decoding up and running again. --- libraries/metavoxels/src/Spanner.cpp | 223 ++++++++++++++++++++------- 1 file changed, 169 insertions(+), 54 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 908226367e..5166777db1 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -604,10 +604,10 @@ HeightfieldData::HeightfieldData(int width) : _width(width) { } -const int HEIGHTFIELD_HEIGHT_HEADER_SIZE = sizeof(qint32) * 4; +const int HEIGHTFIELD_DATA_HEADER_SIZE = sizeof(qint32) * 4; static QByteArray encodeHeightfieldHeight(int offsetX, int offsetY, int width, int height, const QVector& contents) { - QByteArray inflated(HEIGHTFIELD_HEIGHT_HEADER_SIZE, 0); + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; @@ -625,9 +625,9 @@ static QVector decodeHeightfieldHeight(const QByteArray& encoded, int& offsetY = *header++; width = *header++; height = *header++; - int payloadSize = inflated.size() - HEIGHTFIELD_HEIGHT_HEADER_SIZE; + int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; QVector decoded(payloadSize / sizeof(quint16)); - memcpy(decoded.data(), inflated.constData() + HEIGHTFIELD_HEIGHT_HEADER_SIZE, payloadSize); + memcpy(decoded.data(), inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE, payloadSize); return decoded; } @@ -650,6 +650,24 @@ HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const Heightfield reference->setDeltaData(DataBlockPointer(this)); _width = reference->getWidth(); _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QVector delta = decodeHeightfieldHeight(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _width = width; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const quint16* src = delta.constData(); + quint16* dest = _contents.data() + minY * _width + minX; + for (int y = 0; y < height; y++, src += width, dest += _width) { + memcpy(dest, src, width * sizeof(quint16)); + } } void HeightfieldHeight::write(Bitstream& out) { @@ -668,7 +686,39 @@ void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointe } QMutexLocker locker(&reference->getEncodedDeltaMutex()); if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - + int height = _contents.size() / _width; + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const quint16* src = _contents.constData(); + const quint16* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QVector delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QVector(deltaWidth * deltaHeight); + quint16* dest = delta.data(); + src = _contents.constData() + minY * _width + minX; + for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { + memcpy(dest, src, deltaWidth * sizeof(quint16)); + } + } + reference->setEncodedDelta(encodeHeightfieldHeight(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); @@ -810,47 +860,25 @@ void HeightfieldHeightEditor::clear() { _clear->setEnabled(false); } -enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; - -static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = false) { - if (image.isNull()) { - return QByteArray(1, NULL_HEIGHTFIELD_IMAGE); - } - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - const int JPEG_ENCODE_THRESHOLD = 16; - if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD && !lossless) { - qint32 offsetX = image.offset().x(), offsetY = image.offset().y(); - buffer.write((char*)&offsetX, sizeof(qint32)); - buffer.write((char*)&offsetY, sizeof(qint32)); - image.save(&buffer, "JPG"); - return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data()); - - } else { - buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE); - image.save(&buffer, "PNG"); - return buffer.data(); - } +static QByteArray encodeHeightfieldColor(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + inflated.append(contents); + return qCompress(inflated); } -static QImage decodeHeightfieldImage(const QByteArray& data) { - switch (data.at(0)) { - case NULL_HEIGHTFIELD_IMAGE: - default: - return QImage(); - - case NORMAL_HEIGHTFIELD_IMAGE: - return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1)); - - case DEFLATED_HEIGHTFIELD_IMAGE: { - QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1); - const int OFFSET_SIZE = sizeof(qint32) * 2; - QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE); - const qint32* offsets = (const qint32*)inflated.constData(); - image.setOffset(QPoint(offsets[0], offsets[1])); - return image; - } - } +static QByteArray decodeHeightfieldColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + return inflated.mid(HEIGHTFIELD_DATA_HEADER_SIZE); } HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : @@ -872,14 +900,30 @@ HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldCo reference->setDeltaData(DataBlockPointer(this)); _width = reference->getWidth(); _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QByteArray delta = decodeHeightfieldColor(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _width = width; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const char* src = delta.constData(); + char* dest = _contents.data() + (minY * _width + minX) * DataBlock::COLOR_BYTES; + for (int y = 0; y < height; y++, src += width * DataBlock::COLOR_BYTES, dest += _width * DataBlock::COLOR_BYTES) { + memcpy(dest, src, width * DataBlock::COLOR_BYTES); + } } void HeightfieldColor::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - QImage image; - image = QImage((uchar*)_contents.data(), _width, _contents.size() / (_width * COLOR_BYTES), QImage::Format_RGB888); - _encoded = encodeHeightfieldImage(image); + _encoded = encodeHeightfieldColor(0, 0, _width, _contents.size() / (_width * DataBlock::COLOR_BYTES), _contents); } out << _encoded.size(); out.writeAligned(_encoded); @@ -892,13 +936,48 @@ void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& } QMutexLocker locker(&reference->getEncodedDeltaMutex()); if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - + int height = _contents.size() / (_width * DataBlock::COLOR_BYTES); + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++, src += DataBlock::COLOR_BYTES, ref += DataBlock::COLOR_BYTES) { + if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QByteArray delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QByteArray(deltaWidth * deltaHeight * DataBlock::COLOR_BYTES, 0); + char* dest = delta.data(); + src = _contents.constData() + (minY * _width + minX) * DataBlock::COLOR_BYTES; + for (int y = 0; y < deltaHeight; y++, src += _width * DataBlock::COLOR_BYTES, + dest += deltaWidth * DataBlock::COLOR_BYTES) { + memcpy(dest, src, deltaWidth * DataBlock::COLOR_BYTES); + } + } + reference->setEncodedDelta(encodeHeightfieldColor(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); } void HeightfieldColor::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldColor(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); } Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) { @@ -991,10 +1070,8 @@ void HeightfieldColorEditor::clear() { _clear->setEnabled(false); } -const int HEIGHTFIELD_MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; - static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_MATERIAL_HEADER_SIZE, 0); + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; @@ -1011,7 +1088,7 @@ static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offs offsetY = *header++; width = *header++; height = *header++; - return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); + return inflated.mid(HEIGHTFIELD_DATA_HEADER_SIZE); } HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, @@ -1044,6 +1121,7 @@ HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const Heightf } if (offsetX == 0) { _contents = delta; + _width = width; return; } int minX = offsetX - 1; @@ -1066,13 +1144,50 @@ void HeightfieldMaterial::write(Bitstream& out) { } void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) { - if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + if (!reference) { write(out); return; } QMutexLocker locker(&reference->getEncodedDeltaMutex()); if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - + if (reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + reference->setEncodedDelta(encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents)); + + } else { + int height = _contents.size() / _width; + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QByteArray delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QByteArray(deltaWidth * deltaHeight, 0); + char* dest = delta.data(); + src = _contents.constData() + minY * _width + minX; + for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { + memcpy(dest, src, deltaWidth); + } + } + reference->setEncodedDelta(encodeHeightfieldMaterial(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + } + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); From 2f0ea47e5a86fbc435fe5413701f34b6db7357e7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Nov 2014 20:23:35 -0800 Subject: [PATCH 12/38] Use a magic number and version on the metavoxel file, put it in the resources directory. --- .../src/metavoxels/MetavoxelServer.cpp | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 81e86dbf11..622ae87ce6 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -9,7 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include #include #include #include @@ -304,19 +306,34 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : _server(server) { } -const char* SAVE_FILE = "metavoxels.dat"; +const char* SAVE_FILE = "/resources/metavoxels.dat"; + +const int FILE_MAGIC = 0xDADAFACE; +const int FILE_VERSION = 1; void MetavoxelPersister::load() { - QFile file(SAVE_FILE); + QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; + QFile file(path); if (!file.exists()) { return; } MetavoxelData data; { - QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "..."; + QDebug debug = qDebug() << "Reading from" << path << "..."; file.open(QIODevice::ReadOnly); QDataStream inStream(&file); Bitstream in(inStream); + int magic, version; + in >> magic; + if (magic != FILE_MAGIC) { + debug << "wrong file magic: " << magic; + return; + } + in >> version; + if (version != FILE_VERSION) { + debug << "wrong file version: " << version; + return; + } try { in >> data; } catch (const BitstreamException& e) { @@ -330,12 +347,17 @@ void MetavoxelPersister::load() { } void MetavoxelPersister::save(const MetavoxelData& data) { - QDebug debug = qDebug() << "Writing to" << SAVE_FILE << "..."; - QSaveFile file(SAVE_FILE); + QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; + QDir directory = QFileInfo(path).dir(); + if (!directory.exists()) { + directory.mkpath("."); + } + QDebug debug = qDebug() << "Writing to" << path << "..."; + QSaveFile file(path); file.open(QIODevice::WriteOnly); QDataStream outStream(&file); Bitstream out(outStream); - out << data; + out << FILE_MAGIC << FILE_VERSION << data; out.flush(); file.commit(); debug << "done."; From dd87a452114fa21e4193fb48c929db199187681a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Nov 2014 16:11:48 -0800 Subject: [PATCH 13/38] Fix for server saving, use Paeth filter before compressing heightfield data. --- .../src/metavoxels/MetavoxelServer.cpp | 8 +- .../src/metavoxels/MetavoxelServer.h | 2 +- libraries/metavoxels/src/Spanner.cpp | 118 ++++++++++++++++-- 3 files changed, 117 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 622ae87ce6..314ffb28e4 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -35,14 +35,16 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { setData(data); } -void MetavoxelServer::setData(const MetavoxelData& data) { +void MetavoxelServer::setData(const MetavoxelData& data, bool loaded) { if (_data == data) { return; } emit dataChanged(_data = data); - if (!_savedDataInitialized) { + if (loaded) { _savedData = data; + + } else if (!_savedDataInitialized) { _savedDataInitialized = true; // start the save timer @@ -340,7 +342,7 @@ void MetavoxelPersister::load() { debug << "failed, " << e.getDescription(); return; } - QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data)); + QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data), Q_ARG(bool, true)); debug << "done."; } data.dumpStats(); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index a4facb5426..2d1529b1b1 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -36,7 +36,7 @@ public: const MetavoxelData& getData() const { return _data; } - Q_INVOKABLE void setData(const MetavoxelData& data); + Q_INVOKABLE void setData(const MetavoxelData& data, bool loaded = false); virtual void run(); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 5166777db1..fd657903c3 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -613,7 +613,31 @@ static QByteArray encodeHeightfieldHeight(int offsetX, int offsetY, int width, i *header++ = offsetY; *header++ = width; *header++ = height; - inflated.append((const char*)contents.constData(), contents.size() * sizeof(quint16)); + if (!contents.isEmpty()) { + // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) + QVector filteredContents(contents.size()); + const quint16* src = contents.constData(); + quint16* dest = filteredContents.data(); + *dest++ = *src++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + *dest = *src - src[-1]; + } + for (int y = 1; y < height; y++) { + *dest++ = *src - src[-width]; + src++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + int a = src[-1]; + int b = src[-width]; + int c = src[-width - 1]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + inflated.append((const char*)filteredContents.constData(), filteredContents.size() * sizeof(quint16)); + } return qCompress(inflated); } @@ -626,9 +650,30 @@ static QVector decodeHeightfieldHeight(const QByteArray& encoded, int& width = *header++; height = *header++; int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; - QVector decoded(payloadSize / sizeof(quint16)); - memcpy(decoded.data(), inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE, payloadSize); - return decoded; + QVector unfiltered(payloadSize / sizeof(quint16)); + if (!unfiltered.isEmpty()) { + quint16* dest = unfiltered.data(); + const quint16* src = (const quint16*)(inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE); + *dest++ = *src++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + *dest = *src + dest[-1]; + } + for (int y = 1; y < height; y++) { + *dest = (*src++) + dest[-width]; + dest++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + int a = dest[-1]; + int b = dest[-width]; + int c = dest[-width - 1]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + } + return unfiltered; } HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents) : @@ -861,13 +906,42 @@ void HeightfieldHeightEditor::clear() { } static QByteArray encodeHeightfieldColor(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE + contents.size(), 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; *header++ = width; *header++ = height; - inflated.append(contents); + if (!contents.isEmpty()) { + // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) + const uchar* src = (const uchar*)contents.constData(); + uchar* dest = (uchar*)inflated.data() + HEIGHTFIELD_DATA_HEADER_SIZE; + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + int stride = width * DataBlock::COLOR_BYTES; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + *dest = *src - src[-DataBlock::COLOR_BYTES]; + } + for (int y = 1; y < height; y++) { + *dest++ = *src - src[-stride]; + src++; + *dest++ = *src - src[-stride]; + src++; + *dest++ = *src - src[-stride]; + src++; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + int a = src[-DataBlock::COLOR_BYTES]; + int b = src[-stride]; + int c = src[-stride - DataBlock::COLOR_BYTES]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + } return qCompress(inflated); } @@ -878,7 +952,37 @@ static QByteArray decodeHeightfieldColor(const QByteArray& encoded, int& offsetX offsetY = *header++; width = *header++; height = *header++; - return inflated.mid(HEIGHTFIELD_DATA_HEADER_SIZE); + QByteArray contents(inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE, 0); + if (!contents.isEmpty()) { + const uchar* src = (const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; + uchar* dest = (uchar*)contents.data(); + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + int stride = width * DataBlock::COLOR_BYTES; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + *dest = *src + dest[-DataBlock::COLOR_BYTES]; + } + for (int y = 1; y < height; y++) { + *dest = (*src++) + dest[-stride]; + dest++; + *dest = (*src++) + dest[-stride]; + dest++; + *dest = (*src++) + dest[-stride]; + dest++; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + int a = dest[-DataBlock::COLOR_BYTES]; + int b = dest[-stride]; + int c = dest[-stride - DataBlock::COLOR_BYTES]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + } + return contents; } HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : From 75e1cf8a7b3090444a1b6f7fe55e624d84cf6d8f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Nov 2014 18:00:25 -0800 Subject: [PATCH 14/38] Migrate getHeightfieldHeight to spanner heightfields. --- interface/src/MetavoxelSystem.cpp | 83 ------------------- interface/src/MetavoxelSystem.h | 2 - .../metavoxels/src/MetavoxelClientManager.cpp | 44 ++++++++++ .../metavoxels/src/MetavoxelClientManager.h | 2 + libraries/metavoxels/src/Spanner.cpp | 81 ++++++++++++++++++ libraries/metavoxels/src/Spanner.h | 13 ++- 6 files changed, 137 insertions(+), 88 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 36f6a690af..4c1b6d35ac 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -393,89 +393,6 @@ bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, con return true; } -class HeightfieldHeightVisitor : public MetavoxelVisitor { -public: - - float height; - - HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); - - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _location; -}; - -HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : - MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), - height(-FLT_MAX), - _location(location) { -} - -static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); - -int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { - glm::vec3 relative = _location - info.minimum; - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || - height >= info.minimum.y + info.size) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return REVERSE_ORDER; - } - const HeightfieldBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - const QByteArray& contents = buffer->getHeight(); - const uchar* src = (const uchar*)contents.constData(); - int size = glm::sqrt((float)contents.size()); - int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; - int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; - relative *= unextendedSize / info.size; - relative.x += HeightfieldBuffer::HEIGHT_BORDER; - relative.z += HeightfieldBuffer::HEIGHT_BORDER; - - // find the bounds of the cell containing the point and the shared vertex heights - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - float upperLeft = src[floorZ * size + floorX]; - float lowerRight = src[ceilZ * size + ceilX]; - float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - float upperRight = src[floorZ * size + ceilX]; - interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), - (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - float lowerLeft = src[ceilZ * size + floorX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); - } - if (interpolatedHeight == 0.0f) { - return STOP_RECURSION; // ignore zero values - } - - // convert the interpolated height into world space - height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); - return SHORT_CIRCUIT; -} - -float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { - HeightfieldHeightVisitor visitor(getLOD(), location); - guideToAugmented(visitor); - return visitor.height; -} - void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) }; applyEdit(edit, true); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index f194e3d0f9..80051f6de8 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -68,8 +68,6 @@ public: bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); - Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color); Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 353e6e8f35..482db1205c 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -63,6 +63,50 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } +class HeightfieldHeightVisitor : public SpannerVisitor { +public: + + float height; + + HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); + + virtual bool visit(Spanner* spanner); + virtual int visit(MetavoxelInfo& info); + +private: + + glm::vec3 _location; +}; + +HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), lod), + height(-FLT_MAX), + _location(location) { +} + +bool HeightfieldHeightVisitor::visit(Spanner* spanner) { + height = qMax(height, spanner->getHeight(_location)); + return true; +} + +static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); + +int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { + if (_location.x < info.minimum.x || _location.z < info.minimum.z || _location.x > info.minimum.x + info.size || + _location.z > info.minimum.z + info.size) { + return STOP_RECURSION; + } + SpannerVisitor::visit(info); + return (height == -FLT_MAX) ? (info.isLeaf ? STOP_RECURSION : REVERSE_ORDER) : SHORT_CIRCUIT; +} + +float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) { + HeightfieldHeightVisitor visitor(getLOD(), location); + guide(visitor); + return visitor.height; +} + void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) { MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldHeightEdit(position, radius, height)) }; applyEdit(edit, true); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index bdf56400b0..b184f36d5a 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -37,6 +37,8 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); + Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); + Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index fd657903c3..0a6504af12 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -96,6 +96,10 @@ SpannerRenderer* Spanner::getRenderer() { return _renderer; } +float Spanner::getHeight(const glm::vec3& location) const { + return -FLT_MAX; +} + bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { return _bounds.findRayIntersection(origin, direction, distance); } @@ -600,6 +604,8 @@ bool TempHeightfield::intersects(const glm::vec3& start, const glm::vec3& end, f return false; } +const int HeightfieldData::SHARED_EDGE = 1; + HeightfieldData::HeightfieldData(int width) : _width(width) { } @@ -676,6 +682,9 @@ static QVector decodeHeightfieldHeight(const QByteArray& encoded, int& return unfiltered; } +const int HeightfieldHeight::HEIGHT_BORDER = 1; +const int HeightfieldHeight::HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER; + HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents) : HeightfieldData(width), _contents(contents) { @@ -1385,6 +1394,78 @@ void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) { } } +float Heightfield::getHeight(const glm::vec3& location) const { + if (!_height) { + return -FLT_MAX; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::vec3 relative = glm::inverse(getRotation()) * (location - getTranslation()) * glm::vec3(1.0f / getScale(), + 1.0f, 1.0f / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > 1.0f || relative.z > 1.0f) { + return -FLT_MAX; + } + relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER; + relative.z = relative.z * innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + // find the bounds of the cell containing the point and the shared vertex heights + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + float upperRight = src[floorZ * width + ceilX]; + interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), + (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + float lowerLeft = src[ceilZ * width + floorX]; + interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); + } + if (interpolatedHeight == 0.0f) { + return -FLT_MAX; // ignore zero values + } + + // convert the interpolated height into world space + return getTranslation().y + interpolatedHeight * getScale() * _aspectY / numeric_limits::max(); +} + +bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + if (!_height) { + return false; + } + float boundsDistance; + if (!getBounds().findRayIntersection(origin, direction, boundsDistance)) { + return false; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + int height = contents.size() / width; + + glm::quat inverseRotation = glm::inverse(getRotation()); + glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); + glm::vec3 dir = inverseRotation * direction * inverseScale; + glm::vec3 entry = inverseRotation * (origin + direction * boundsDistance - getTranslation()) * inverseScale; + + return false; + +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 6fdf93a85a..54918fe502 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -55,6 +55,9 @@ public: /// Returns a pointer to the renderer, creating it if necessary. SpannerRenderer* getRenderer(); + /// Finds the height at the specified location, or returns -FLT_MAX for none. + virtual float getHeight(const glm::vec3& location) const; + /// Finds the intersection between the described ray and this spanner. virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; @@ -303,7 +306,7 @@ private: class HeightfieldData : public DataBlock { public: - static const int SHARED_EDGE = 1; + static const int SHARED_EDGE; HeightfieldData(int width = 0); @@ -320,8 +323,8 @@ typedef QExplicitlySharedDataPointer HeightfieldHeightPointer class HeightfieldHeight : public HeightfieldData { public: - static const int HEIGHT_BORDER = 1; - static const int HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER; + static const int HEIGHT_BORDER; + static const int HEIGHT_EXTENSION; HeightfieldHeight(int width, const QVector& contents); HeightfieldHeight(Bitstream& in, int bytes); @@ -501,6 +504,10 @@ public: void setMaterial(const HeightfieldMaterialPointer& material); const HeightfieldMaterialPointer& getMaterial() const { return _material; } + virtual float getHeight(const glm::vec3& location) const; + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + signals: void aspectYChanged(float aspectY); From b2ac33db16c6952aca52d7d5dc8fb657e5a36bc1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Nov 2014 19:53:16 -0800 Subject: [PATCH 15/38] Ray tests for heightfield spanners. --- interface/src/MetavoxelSystem.cpp | 182 +----------------- interface/src/MetavoxelSystem.h | 2 - interface/src/ui/MetavoxelEditor.cpp | 2 +- .../metavoxels/src/MetavoxelClientManager.cpp | 37 ++++ .../metavoxels/src/MetavoxelClientManager.h | 2 + libraries/metavoxels/src/Spanner.cpp | 134 ++++++++++++- 6 files changed, 175 insertions(+), 184 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4c1b6d35ac..0a7986ed07 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -171,185 +171,6 @@ void MetavoxelSystem::refreshVoxelData() { } } -class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { -public: - - float intersectionDistance; - - RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info, float distance); -}; - -RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, - const glm::vec3& direction, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), - intersectionDistance(FLT_MAX) { -} - -static const int EIGHT_BIT_MAXIMUM = 255; -static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; - -int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { - if (!info.isLeaf) { - return _order; - } - const HeightfieldBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - const QByteArray& contents = buffer->getHeight(); - const uchar* src = (const uchar*)contents.constData(); - int size = glm::sqrt((float)contents.size()); - int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; - int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; - float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL; - - // find the initial location in heightfield coordinates - glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size; - entry.x += HeightfieldBuffer::HEIGHT_BORDER; - entry.z += HeightfieldBuffer::HEIGHT_BORDER; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (_direction.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (_direction.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - bool withinBounds = true; - float accumulatedDistance = 0.0f; - while (withinBounds) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - float upperLeft = src[floorZ * size + floorX] * heightScale; - float upperRight = src[floorZ * size + ceilX] * heightScale; - float lowerLeft = src[ceilZ * size + floorX] * heightScale; - float lowerRight = src[ceilZ * size + ceilX] * heightScale; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (_direction.x > 0.0f) { - xDistance = (ceils.x - entry.x) / _direction.x; - } else if (_direction.x < 0.0f) { - xDistance = (floors.x - entry.x) / _direction.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (_direction.z > 0.0f) { - zDistance = (ceils.z - entry.z) / _direction.z; - } else if (_direction.z < 0.0f) { - zDistance = (floors.z - entry.z) / _direction.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - if (_direction.y > 0.0f) { - return SHORT_CIRCUIT; // line points upwards; no collisions possible - } - withinBounds = false; // line points downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * _direction; - withinBounds = (exit.y >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest); - if (exitDistance == xDistance) { - if (_direction.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highest; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (_direction.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highest; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, _direction); - if (lowerProduct < 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * _direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - intersectionDistance = qMin(intersectionDistance, distance + - (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); - return SHORT_CIRCUIT; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, _direction); - if (upperProduct < 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * _direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - intersectionDistance = qMin(intersectionDistance, distance + - (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); - return SHORT_CIRCUIT; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return STOP_RECURSION; -} - -bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin, - const glm::vec3& direction, float& distance) { - RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); - guideToAugmented(visitor); - if (visitor.intersectionDistance == FLT_MAX) { - return false; - } - distance = visitor.intersectionDistance; - return true; -} - class RayVoxelIntersectionVisitor : public RayIntersectionVisitor { public: @@ -917,6 +738,9 @@ public: const int SPLAT_COUNT = 4; const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; +static const int EIGHT_BIT_MAXIMUM = 255; +static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; + void HeightfieldBuffer::render(bool cursor) { // initialize textures, etc. on first render if (_heightTextureID == 0) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 80051f6de8..b38370017a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -64,8 +64,6 @@ public: void renderVoxelCursor(const glm::vec3& position, float radius); - bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 2cbd445f80..d13385d4eb 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -900,7 +900,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin } bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("HeightfieldAttribute"); + return attribute->inherits("SpannerSetAttribute"); } void HeightfieldBrushTool::render() { diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 482db1205c..6569e85c62 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -63,6 +63,43 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } +class RayHeightfieldIntersectionVisitor : public RaySpannerIntersectionVisitor { +public: + + float intersectionDistance; + + RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); + + virtual bool visitSpanner(Spanner* spanner, float distance); +}; + +RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, + const glm::vec3& direction, const MetavoxelLOD& lod) : + RaySpannerIntersectionVisitor(origin, direction, QVector() << + AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), lod), + intersectionDistance(FLT_MAX) { +} + +bool RayHeightfieldIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { + if (qobject_cast(spanner)) { + intersectionDistance = distance; + return false; + } + return true; +} + +bool MetavoxelClientManager::findFirstRayHeightfieldIntersection(const glm::vec3& origin, + const glm::vec3& direction, float& distance) { + RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); + guide(visitor); + if (visitor.intersectionDistance == FLT_MAX) { + return false; + } + distance = visitor.intersectionDistance; + return true; +} + class HeightfieldHeightVisitor : public SpannerVisitor { public: diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index b184f36d5a..8d029c172f 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -36,6 +36,8 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); + + bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 0a6504af12..204f139422 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1455,15 +1455,145 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& } int width = _height->getWidth(); const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; glm::quat inverseRotation = glm::inverse(getRotation()); - glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); + glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), + innerHeight / (getScale() * _aspectZ)); glm::vec3 dir = inverseRotation * direction * inverseScale; glm::vec3 entry = inverseRotation * (origin + direction * boundsDistance - getTranslation()) * inverseScale; - return false; + entry.x += HeightfieldHeight::HEIGHT_BORDER; + entry.z += HeightfieldHeight::HEIGHT_BORDER; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (dir.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (dir.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + bool withinBounds = true; + float accumulatedDistance = 0.0f; + while (withinBounds) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float upperRight = src[floorZ * width + ceilX]; + float lowerLeft = src[ceilZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (dir.x > 0.0f) { + xDistance = (ceils.x - entry.x) / dir.x; + } else if (dir.x < 0.0f) { + xDistance = (floors.x - entry.x) / dir.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (dir.z > 0.0f) { + zDistance = (ceils.z - entry.z) / dir.z; + } else if (dir.z < 0.0f) { + zDistance = (floors.z - entry.z) / dir.z; + } + + // the exit distance is the lower of those two + float exitDistance = qMin(xDistance, zDistance); + glm::vec3 exit, nextFloors = floors, nextCeils = ceils; + if (exitDistance == FLT_MAX) { + if (dir.y > 0.0f) { + return false; // line points upwards; no collisions possible + } + withinBounds = false; // line points downwards; check this cell only + + } else { + // find the exit point and the next cell, and determine whether it's still within the bounds + exit = entry + exitDistance * dir; + withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); + if (exitDistance == xDistance) { + if (dir.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highestX; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (dir.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highestZ; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.z -= 1.0f; + } + } + // check the vertical range of the ray against the ranges of the cell heights + if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || + qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + continue; + } + } + // having passed the bounds check, we must check against the planes + glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); + + // first check the triangle including the Z+ segment + glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); + float lowerProduct = glm::dot(lowerNormal, dir); + if (lowerProduct < 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + distance = boundsDistance + accumulatedDistance + planeDistance; + return true; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, dir); + if (upperProduct < 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + distance = boundsDistance + accumulatedDistance + planeDistance; + return true; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return false; } QByteArray Heightfield::getRendererClassName() const { From b879471a4b563309a844660c4b30607c569ac7bb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Nov 2014 20:55:36 -0800 Subject: [PATCH 16/38] Cursor rendering on spanners. --- interface/src/MetavoxelSystem.cpp | 323 +++++++++--------- interface/src/MetavoxelSystem.h | 8 +- interface/src/ui/MetavoxelEditor.cpp | 7 +- .../metavoxels/src/MetavoxelClientManager.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 10 +- libraries/metavoxels/src/Spanner.h | 9 +- 6 files changed, 188 insertions(+), 171 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 0a7986ed07..1b1ed730d9 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -244,10 +244,12 @@ void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const applyMaterialEdit(edit, true); } -class CursorRenderVisitor : public MetavoxelVisitor { +class SpannerCursorRenderVisitor : public SpannerVisitor { public: - CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); + SpannerCursorRenderVisitor(const Box& bounds); + + virtual bool visit(Spanner* spanner); virtual int visit(MetavoxelInfo& info); @@ -256,23 +258,20 @@ private: Box _bounds; }; -CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : - MetavoxelVisitor(QVector() << attribute), +SpannerCursorRenderVisitor::SpannerCursorRenderVisitor(const Box& bounds) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), _bounds(bounds) { } -int CursorRenderVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; +bool SpannerCursorRenderVisitor::visit(Spanner* spanner) { + if (spanner->isHeightfield()) { + spanner->getRenderer()->render(true); } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - BufferDataPointer buffer = info.inputValues.at(0).getInlineValue(); - if (buffer) { - buffer->render(true); - } - return STOP_RECURSION; + return true; +} + +int SpannerCursorRenderVisitor::visit(MetavoxelInfo& info) { + return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION; } void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) { @@ -299,9 +298,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), - Box(position - extents, position + extents)); - guideToAugmented(visitor); + SpannerCursorRenderVisitor visitor(Box(position - extents, position + extents)); + guide(visitor); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); @@ -313,6 +311,34 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glDepthFunc(GL_LESS); } +class BufferCursorRenderVisitor : public MetavoxelVisitor { +public: + + BufferCursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); + + virtual int visit(MetavoxelInfo& info); + +private: + + Box _bounds; +}; + +BufferCursorRenderVisitor::BufferCursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : + MetavoxelVisitor(QVector() << attribute), + _bounds(bounds) { +} + +int BufferCursorRenderVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + BufferData* buffer = info.inputValues.at(0).getInlineValue().data(); + if (buffer) { + buffer->render(true); + } + return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; +} + void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) { glDepthFunc(GL_LEQUAL); glEnable(GL_CULL_FACE); @@ -337,22 +363,11 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glm::vec3 extents(radius, radius, radius); Box bounds(position - extents, position + extents); - CursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds); + BufferCursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds); guideToAugmented(voxelVisitor); DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); - - CursorRenderVisitor heightfieldVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), - bounds); - guideToAugmented(heightfieldVisitor); - - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_POLYGON_OFFSET_FILL); @@ -2464,8 +2479,7 @@ int SpannerRenderVisitor::visit(MetavoxelInfo& info) { } bool SpannerRenderVisitor::visit(Spanner* spanner) { - const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); - spanner->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DEFAULT_MODE); + spanner->getRenderer()->render(); return true; } @@ -2597,10 +2611,10 @@ SphereRenderer::SphereRenderer() { } -void SphereRenderer::render(const glm::vec4& color, Mode mode) { +void SphereRenderer::render(bool cursor) { Sphere* sphere = static_cast(_spanner); - const QColor& ownColor = sphere->getColor(); - glColor4f(ownColor.redF() * color.r, ownColor.greenF() * color.g, ownColor.blueF() * color.b, ownColor.alphaF() * color.a); + const QColor& color = sphere->getColor(); + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glPushMatrix(); const glm::vec3& translation = sphere->getTranslation(); @@ -2617,10 +2631,10 @@ void SphereRenderer::render(const glm::vec4& color, Mode mode) { CuboidRenderer::CuboidRenderer() { } -void CuboidRenderer::render(const glm::vec4& color, Mode mode) { +void CuboidRenderer::render(bool cursor) { Cuboid* cuboid = static_cast(_spanner); - const QColor& ownColor = cuboid->getColor(); - glColor4f(ownColor.redF() * color.r, ownColor.greenF() * color.g, ownColor.blueF() * color.b, ownColor.alphaF() * color.a); + const QColor& color = cuboid->getColor(); + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glPushMatrix(); const glm::vec3& translation = cuboid->getTranslation(); @@ -2668,21 +2682,8 @@ void StaticModelRenderer::simulate(float deltaTime) { _model->simulate(deltaTime); } -void StaticModelRenderer::render(const glm::vec4& color, Mode mode) { - switch (mode) { - case DIFFUSE_MODE: - _model->render(color.a, Model::DIFFUSE_RENDER_MODE); - break; - - case NORMAL_MODE: - _model->render(color.a, Model::NORMAL_RENDER_MODE); - break; - - default: - _model->render(color.a); - break; - } - _model->render(color.a); +void StaticModelRenderer::render(bool cursor) { + _model->render(); } bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { @@ -2756,7 +2757,7 @@ void HeightfieldRenderer::init(Spanner* spanner) { connect(heightfield, &Heightfield::materialChanged, this, &HeightfieldRenderer::applyMaterial); } -void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { +void HeightfieldRenderer::render(bool cursor) { // create the buffer objects lazily Heightfield* heightfield = static_cast(_spanner); if (!heightfield->getHeight() || !Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { @@ -2830,130 +2831,138 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) { float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ(); glScalef(xScale, xScale * heightfield->getAspectY(), zScale); - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - HeightfieldPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - float sDistance = 2.0f / (innerWidth - 1); - float tDistance = 2.0f / (innerHeight - 1); - float distanceProduct = -sDistance * tDistance; - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / width, 1.0f / height, - sDistance / distanceProduct, tDistance / distanceProduct); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)width / innerWidth, - (float)height / innerHeight); - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); - - glDisable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - - if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); + if (cursor) { + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.heightScale, 1.0f / width, 1.0f / height); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureScale, (float)width / innerWidth, (float)height / innerHeight); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(locations.splatTextureOffset, - glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, - glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); - - glBindTexture(GL_TEXTURE_2D, _materialTextureID); + } else { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); - const QVector& materials = heightfield->getMaterial()->getMaterials(); - for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { - QVector4D scalesS, scalesT; + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + float sDistance = 2.0f / (innerWidth - 1); + float tDistance = 2.0f / (innerHeight - 1); + float distanceProduct = -sDistance * tDistance; + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / width, 1.0f / height, + sDistance / distanceProduct, tDistance / distanceProduct); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)width / innerWidth, + (float)height / innerHeight); - for (int j = 0; j < SPLAT_COUNT; j++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); - int index = i + j; - if (index < _networkTextures.size()) { - const NetworkTexturePointer& texture = _networkTextures.at(index); - if (texture) { - MaterialObject* material = static_cast(materials.at(index).data()); - scalesS[j] = xScale / material->getScaleS(); - scalesT[j] = zScale / material->getScaleT(); - glBindTexture(GL_TEXTURE_2D, texture->getID()); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + + if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); + const DefaultMetavoxelRendererImplementation::SplatLocations& locations = + DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.heightScale, 1.0f / width, 1.0f / height); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureScale, (float)width / innerWidth, (float)height / innerHeight); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(locations.splatTextureOffset, + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); + + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + + const QVector& materials = heightfield->getMaterial()->getMaterials(); + for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { + QVector4D scalesS, scalesT; + + for (int j = 0; j < SPLAT_COUNT; j++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); + int index = i + j; + if (index < _networkTextures.size()) { + const NetworkTexturePointer& texture = _networkTextures.at(index); + if (texture) { + MaterialObject* material = static_cast(materials.at(index).data()); + scalesS[j] = xScale / material->getScaleS(); + scalesT[j] = zScale / material->getScaleT(); + glBindTexture(GL_TEXTURE_2D, texture->getID()); + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } } else { glBindTexture(GL_TEXTURE_2D, 0); } - } else { - glBindTexture(GL_TEXTURE_2D, 0); } + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.splatTextureScalesS, scalesS); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.splatTextureScalesT, scalesT); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureValueMinima, + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureValueMaxima, + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); } - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesS, scalesS); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesT, scalesT); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMinima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMaxima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - } - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthMask(true); + glDepthFunc(GL_LESS); } - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_CULL_FACE); - glActiveTexture(GL_TEXTURE0); - - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); - - glDisable(GL_POLYGON_OFFSET_FILL); - glDepthMask(true); - glDepthFunc(GL_LESS); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); } glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_CULL_FACE); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); bufferPair.first.release(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index b38370017a..a911db123a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -407,7 +407,7 @@ public: Q_INVOKABLE SphereRenderer(); - virtual void render(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); }; /// Renders cuboids. @@ -418,7 +418,7 @@ public: Q_INVOKABLE CuboidRenderer(); - virtual void render(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); }; /// Renders static models. @@ -431,7 +431,7 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; private slots: @@ -456,7 +456,7 @@ public: virtual ~HeightfieldRenderer(); virtual void init(Spanner* spanner); - virtual void render(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); private slots: diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index d13385d4eb..f0634f5c40 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -634,9 +634,7 @@ void PlaceSpannerTool::simulate(float deltaTime) { void PlaceSpannerTool::renderPreview() { Spanner* spanner = static_cast(getSpanner().data()); - QColor color = getColor(); - spanner->getRenderer()->render(glm::vec4(color.redF(), color.greenF(), color.blueF(), 1.0f), - SpannerRenderer::DEFAULT_MODE); + spanner->getRenderer()->render(); } bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { @@ -791,8 +789,7 @@ void ImportHeightfieldTool::simulate(float deltaTime) { } void ImportHeightfieldTool::renderPreview() { - static_cast(_spanner.data())->getRenderer()->render(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), - SpannerRenderer::DEFAULT_MODE); + static_cast(_spanner.data())->getRenderer()->render(); } const int HEIGHTFIELD_BLOCK_SIZE = 32; diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 6569e85c62..660796994b 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -82,7 +82,7 @@ RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm:: } bool RayHeightfieldIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { - if (qobject_cast(spanner)) { + if (spanner->isHeightfield()) { intersectionDistance = distance; return false; } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 204f139422..3184795488 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -96,6 +96,10 @@ SpannerRenderer* Spanner::getRenderer() { return _renderer; } +bool Spanner::isHeightfield() const { + return false; +} + float Spanner::getHeight(const glm::vec3& location) const { return -FLT_MAX; } @@ -150,7 +154,7 @@ void SpannerRenderer::simulate(float deltaTime) { // nothing by default } -void SpannerRenderer::render(const glm::vec4& color, Mode mode) { +void SpannerRenderer::render(bool cursor) { // nothing by default } @@ -1394,6 +1398,10 @@ void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) { } } +bool Heightfield::isHeightfield() const { + return true; +} + float Heightfield::getHeight(const glm::vec3& location) const { if (!_height) { return -FLT_MAX; diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 54918fe502..2c8f0d78ca 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -55,6 +55,9 @@ public: /// Returns a pointer to the renderer, creating it if necessary. SpannerRenderer* getRenderer(); + /// Checks whether this is a heightfield. + virtual bool isHeightfield() const; + /// Finds the height at the specified location, or returns -FLT_MAX for none. virtual float getHeight(const glm::vec3& location) const; @@ -112,13 +115,11 @@ class SpannerRenderer : public QObject { public: - enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE }; - Q_INVOKABLE SpannerRenderer(); virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; protected: @@ -504,6 +505,8 @@ public: void setMaterial(const HeightfieldMaterialPointer& material); const HeightfieldMaterialPointer& getMaterial() const { return _material; } + virtual bool isHeightfield() const; + virtual float getHeight(const glm::vec3& location) const; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; From bba042e823c8ebc551ee671c51acb7e9dfae22a8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Nov 2014 16:52:29 -0800 Subject: [PATCH 17/38] Heightfield material paint bits. --- interface/src/MetavoxelSystem.cpp | 7 +- libraries/metavoxels/src/MetavoxelData.cpp | 39 +++++ libraries/metavoxels/src/MetavoxelData.h | 3 + .../metavoxels/src/MetavoxelMessages.cpp | 137 ++---------------- libraries/metavoxels/src/Spanner.cpp | 110 ++++++++++++++ libraries/metavoxels/src/Spanner.h | 6 + 6 files changed, 172 insertions(+), 130 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9577a04695..5ff179765e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2869,8 +2869,7 @@ void HeightfieldRenderer::render(bool cursor) { glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); @@ -2946,14 +2945,14 @@ void HeightfieldRenderer::render(bool cursor) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); glDisable(GL_POLYGON_OFFSET_FILL); glDepthMask(true); glDepthFunc(GL_LESS); } + glActiveTexture(GL_TEXTURE0); + glDisable(GL_CULL_FACE); glDisableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index cb3c5b94bf..94b3e0be97 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -354,6 +354,45 @@ void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds guide(visitor); } +class SpannerFetchVisitor : public SpannerVisitor { +public: + + SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, QVector& results); + + virtual bool visit(Spanner* spanner); + + virtual int visit(MetavoxelInfo& info); + +private: + + const Box& _bounds; + QVector& _results; +}; + +SpannerFetchVisitor::SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, + QVector& results) : + SpannerVisitor(QVector() << attribute), + _bounds(bounds), + _results(results) { +} + +bool SpannerFetchVisitor::visit(Spanner* spanner) { + if (spanner->getBounds().intersects(_bounds)) { + _results.append(spanner); + } + return true; +} + +int SpannerFetchVisitor::visit(MetavoxelInfo& info) { + return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION; +} + +void MetavoxelData::getIntersecting(const AttributePointer& attribute, const Box& bounds, + QVector& results) { + SpannerFetchVisitor visitor(attribute, bounds, results); + guide(visitor); +} + void MetavoxelData::clear(const AttributePointer& attribute) { MetavoxelNode* node = _roots.take(attribute); if (node) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index b80da9b2a0..7bfd2a7522 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -102,6 +102,9 @@ public: void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); + /// Retrieves all spanners that intersect the specified bounds. + void getIntersecting(const AttributePointer& attribute, const Box& bounds, QVector& results); + /// Clears all data in the specified attribute layer. void clear(const AttributePointer& attribute); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index d40b49a2e6..c7471e7296 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -242,130 +242,6 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av averageColor(averageColor) { } -class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor { -public: - - PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color); - - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _position; - float _radius; - SharedObjectPointer _material; - QColor _color; - Box _bounds; -}; - -PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()), - _position(position), - _radius(radius), - _material(material), - _color(color) { - - const float LARGE_EXTENT = 100000.0f; - glm::vec3 extents(_radius, LARGE_EXTENT, _radius); - _bounds = Box(_position - extents, _position + extents); -} - -int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); - if (colorPointer) { - QByteArray contents(colorPointer->getContents()); - int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); - int highest = size - 1; - float heightScale = size / info.size; - - glm::vec3 center = (_position - info.minimum) * heightScale; - float scaledRadius = _radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // paint all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - int stride = size * DataBlock::COLOR_BYTES; - char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; - float squaredRadius = scaledRadius * scaledRadius; - char red = _color.red(), green = _color.green(), blue = _color.blue(); - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - char* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { - float dx = x - center.x, dz = z - center.z; - if (dx * dx + dz * dz <= squaredRadius) { - dest[0] = red; - dest[1] = green; - dest[2] = blue; - changed = true; - } - } - lineDest += stride; - } - if (changed) { - HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); - info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), - encodeInline(newPointer)); - } - } - - HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue(); - if (materialPointer) { - QVector materials = materialPointer->getMaterials(); - QByteArray contents(materialPointer->getContents()); - uchar materialIndex = getMaterialIndex(_material, materials, contents); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = highest / info.size; - - glm::vec3 center = (_position - info.minimum) * heightScale; - float scaledRadius = _radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // paint all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; - float squaredRadius = scaledRadius * scaledRadius; - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = z - center.z; - if (dx * dx + dz * dz <= squaredRadius) { - *dest = materialIndex; - changed = true; - } - } - lineDest += size; - } - if (changed) { - clearUnusedMaterials(materials, contents); - HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials)); - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); - } - } - return STOP_RECURSION; -} - PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& averageColor) : MaterialEdit(material, averageColor), @@ -374,8 +250,17 @@ PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& posi } void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor); - data.guide(visitor); + glm::vec3 extents(radius, radius, radius); + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), + Box(position - extents, position + extents), results); + + foreach (const SharedObjectPointer& spanner, results) { + Spanner* newSpanner = static_cast(spanner.data())->paint(position, radius, material, averageColor); + if (newSpanner != spanner) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); + } + } } const int VOXEL_BLOCK_SIZE = 16; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 3184795488..0808ae2ed1 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -108,6 +108,10 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire return _bounds.findRayIntersection(origin, direction, distance); } +Spanner* Spanner::paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) { + return this; +} + bool Spanner::hasOwnColors() const { return false; } @@ -1604,6 +1608,112 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& return false; } +Spanner* Heightfield::paint(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int baseWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE; + int baseHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE; + Heightfield* newHeightfield = static_cast(clone(true)); + + int colorWidth = baseWidth, colorHeight = baseHeight; + QByteArray colorContents; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + colorContents = _color->getContents(); + + } else { + colorContents = QByteArray(baseWidth * baseHeight * DataBlock::COLOR_BYTES, 0xFF); + } + + int materialWidth = baseWidth, materialHeight = baseHeight; + QByteArray materialContents; + QVector materials; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + materialContents = _material->getContents(); + materials = _material->getMaterials(); + + } else { + materialContents = QByteArray(baseWidth * baseHeight, 0); + } + + int highestX = colorWidth - 1; + int highestZ = colorHeight - 1; + glm::vec3 inverseScale(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ)); + glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + + glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; + glm::vec3 start = glm::floor(center - extents); + glm::vec3 end = glm::ceil(center + extents); + + // paint all points within the radius + float z = qMax(start.z, 0.0f); + float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + int stride = colorWidth * DataBlock::COLOR_BYTES; + uchar* lineDest = (uchar*)colorContents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; + float squaredRadius = extents.x * extents.x; + float multiplierZ = inverseScale.x / inverseScale.z; + char red = color.red(), green = color.green(), blue = color.blue(); + bool changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + uchar* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + if (dx * dx + dz * dz <= squaredRadius) { + dest[0] = red; + dest[1] = green; + dest[2] = blue; + changed = true; + } + } + lineDest += stride; + } + if (changed) { + newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, colorContents))); + } + + highestX = materialWidth - 1; + highestZ = materialHeight - 1; + inverseScale = glm::vec3(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ)); + center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + + extents = glm::vec3(radius, radius, radius) * inverseScale; + start = glm::floor(center - extents); + end = glm::ceil(center + extents); + + // paint all points within the radius + z = qMax(start.z, 0.0f); + startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + lineDest = (uchar*)materialContents.data() + (int)z * materialWidth + (int)startX; + squaredRadius = extents.x * extents.x; + uchar materialIndex = getMaterialIndex(material, materials, materialContents); + changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + uchar* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + if (dx * dx + dz * dz <= squaredRadius) { + *dest = materialIndex; + changed = true; + } + } + lineDest += materialWidth; + } + if (changed) { + clearUnusedMaterials(materials, materialContents); + newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, + materialContents, materials))); + } + + return newHeightfield; +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 2c8f0d78ca..beaf6c8a7e 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -64,6 +64,10 @@ public: /// Finds the intersection between the described ray and this spanner. virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + /// Attempts to paint on the spanner. + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color); + /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -511,6 +515,8 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color); + signals: void aspectYChanged(float aspectY); From 479bea1eb2bb0be2cebc3b5a7811775089040551 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Nov 2014 17:18:34 -0800 Subject: [PATCH 18/38] Removed heightfield buffer code. --- interface/src/MetavoxelSystem.cpp | 835 +----------------------------- interface/src/MetavoxelSystem.h | 88 ---- 2 files changed, 9 insertions(+), 914 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 5ff179765e..7edf96d1c6 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -58,11 +58,6 @@ void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); - _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( - new BufferDataAttribute("heightfieldBuffer")); - _heightfieldBufferAttribute->setLODThresholdMultiplier( - AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); - _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("voxelBuffer")); _voxelBufferAttribute->setLODThresholdMultiplier( @@ -375,12 +370,6 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glDepthFunc(GL_LESS); } -void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { - glDeleteTextures(1, (GLuint*)&heightID); - glDeleteTextures(1, (GLuint*)&colorID); - glDeleteTextures(1, (GLuint*)&textureID); -} - class MaterialEditApplier : public SignalHandler { public: @@ -670,357 +659,12 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { BufferData::~BufferData() { } -const int HeightfieldBuffer::HEIGHT_BORDER = 1; -const int HeightfieldBuffer::SHARED_EDGE = 1; -const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE; - -HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, - const QByteArray& height, const QByteArray& color, const QByteArray& material, - const QVector& materials) : - _translation(translation), - _scale(scale), - _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), - _colorBounds(_heightBounds), - _materialBounds(_heightBounds), - _height(height), - _color(color), - _material(material), - _materials(materials), - _heightTextureID(0), - _colorTextureID(0), - _materialTextureID(0), - _heightSize(glm::sqrt((float)height.size())), - _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), - _colorSize(glm::sqrt((float)color.size() / DataBlock::COLOR_BYTES)), - _colorIncrement(scale / (_colorSize - SHARED_EDGE)), - _materialSize(glm::sqrt((float)material.size())), - _materialIncrement(scale / (_materialSize - SHARED_EDGE)) { - - _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; - _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; - _heightBounds.maximum.x += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER); - _heightBounds.maximum.z += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER); - - _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; - _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; - - _materialBounds.maximum.x += _materialIncrement * SHARED_EDGE; - _materialBounds.maximum.z += _materialIncrement * SHARED_EDGE; -} - -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(int, _heightTextureID), Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); - } else { - glDeleteTextures(1, &_heightTextureID); - glDeleteTextures(1, &_colorTextureID); - glDeleteTextures(1, &_materialTextureID); - } -} - -QByteArray HeightfieldBuffer::getUnextendedHeight() const { - int srcSize = glm::sqrt(float(_height.size())); - int destSize = srcSize - 3; - QByteArray unextended(destSize * destSize, 0); - const char* src = _height.constData() + srcSize + 1; - char* dest = unextended.data(); - for (int z = 0; z < destSize; z++, src += srcSize, dest += destSize) { - memcpy(dest, src, destSize); - } - return unextended; -} - -QByteArray HeightfieldBuffer::getUnextendedColor(int x, int y) const { - int unextendedSize = _heightSize - HEIGHT_EXTENSION; - QByteArray unextended(unextendedSize * unextendedSize * DataBlock::COLOR_BYTES, 0); - char* dest = unextended.data(); - const char* src = _color.constData() + (y * _colorSize + x) * unextendedSize * DataBlock::COLOR_BYTES; - for (int z = 0; z < unextendedSize; z++, dest += unextendedSize * DataBlock::COLOR_BYTES, - src += _colorSize * DataBlock::COLOR_BYTES) { - memcpy(dest, src, unextendedSize * DataBlock::COLOR_BYTES); - } - return unextended; -} - -class HeightfieldPoint { -public: - glm::vec2 textureCoord; - glm::vec3 vertex; -}; - const int SPLAT_COUNT = 4; const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; static const int EIGHT_BIT_MAXIMUM = 255; static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; -void HeightfieldBuffer::render(bool cursor) { - // initialize textures, etc. on first render - if (_heightTextureID == 0) { - glGenTextures(1, &_heightTextureID); - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - 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()); - - 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(float(_color.size() / DataBlock::COLOR_BYTES)); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); - } - - if (!_material.isEmpty()) { - glGenTextures(1, &_materialTextureID); - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - int materialSize = glm::sqrt(float(_material.size())); - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, materialSize, materialSize, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, _material.constData()); - - _networkTextures.resize(_materials.size()); - for (int i = 0; i < _materials.size(); i++) { - const SharedObjectPointer material = _materials.at(i); - if (material) { - _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( - static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); - } - } - } - } - // create the buffer objects lazily - int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER; - int vertexCount = _heightSize * _heightSize; - int rows = _heightSize - 1; - int indexCount = rows * rows * 3 * 2; - BufferPair& bufferPair = _bufferPairs[_heightSize]; - if (!bufferPair.first.isCreated()) { - QVector vertices(vertexCount); - HeightfieldPoint* point = vertices.data(); - - float vertexStep = 1.0f / (innerSize - 1); - float z = -vertexStep; - float textureStep = 1.0f / _heightSize; - float t = textureStep / 2.0f; - for (int i = 0; i < _heightSize; i++, z += vertexStep, t += textureStep) { - float x = -vertexStep; - float s = textureStep / 2.0f; - const float SKIRT_LENGTH = 0.25f; - float baseY = (i == 0 || i == _heightSize - 1) ? -SKIRT_LENGTH : 0.0f; - for (int j = 0; j < _heightSize; j++, point++, x += vertexStep, s += textureStep) { - point->vertex = glm::vec3(x, (j == 0 || j == _heightSize - 1) ? -SKIRT_LENGTH : baseY, z); - point->textureCoord = glm::vec2(s, t); - } - } - - 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 * _heightSize; - int nextLineIndex = (i + 1) * _heightSize; - for (int j = 0; j < rows; j++) { - *index++ = lineIndex + j; - *index++ = nextLineIndex + j; - *index++ = nextLineIndex + j + 1; - - *index++ = nextLineIndex + j + 1; - *index++ = lineIndex + j + 1; - *index++ = lineIndex + j; - } - } - - 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); - - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - - if (cursor) { - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - } else if (!_materials.isEmpty()) { - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.heightScale, 1.0f / _heightSize); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureScale, (float)_heightSize / innerSize); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureOffset, _translation.x / _scale, _translation.z / _scale); - - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - - for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { - QVector4D scalesS, scalesT; - - for (int j = 0; j < SPLAT_COUNT; j++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); - int index = i + j; - if (index < _networkTextures.size()) { - const NetworkTexturePointer& texture = _networkTextures.at(index); - if (texture) { - MaterialObject* material = static_cast(_materials.at(index).data()); - scalesS[j] = _scale / material->getScaleS(); - scalesT[j] = _scale / material->getScaleT(); - glBindTexture(GL_TEXTURE_2D, texture->getID()); - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesS, scalesS); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesT, scalesT); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMinima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMaxima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - } - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - - glActiveTexture(GL_TEXTURE0); - - glDisable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_ALPHA_TEST); - glDisable(GL_BLEND); - glDepthMask(true); - glDepthFunc(GL_LESS); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - - } else { - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - } - - glBindTexture(GL_TEXTURE_2D, 0); - - glPopMatrix(); - - bufferPair.first.release(); - bufferPair.second.release(); -} - -QHash HeightfieldBuffer::_bufferPairs; - -void HeightfieldPreview::render(const glm::vec3& translation, float scale) const { - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - 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); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - - glPushMatrix(); - glTranslatef(translation.x, translation.y, translation.z); - glScalef(scale, scale, scale); - - foreach (const BufferDataPointer& buffer, _buffers) { - buffer->render(); - } - - glPopMatrix(); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); -} - void VoxelPoint::setNormal(const glm::vec3& normal) { this->normal[0] = (char)(normal.x * 127.0f); this->normal[1] = (char)(normal.y * 127.0f); @@ -1344,446 +988,6 @@ void DefaultMetavoxelRendererImplementation::init() { DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } -class HeightfieldFetchVisitor : public MetavoxelVisitor { -public: - - HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections); - - void init(HeightfieldBuffer* buffer); - - virtual int visit(MetavoxelInfo& info); - virtual bool postVisit(MetavoxelInfo& info); - -private: - - const QVector& _intersections; - HeightfieldBuffer* _buffer; - - QVector _depthFlags; -}; - -enum DepthFlags { HEIGHT_FLAG = 0x01, COLOR_FLAG = 0x02, MATERIAL_FLAG = 0x04 }; - -HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector(), lod), - _intersections(intersections) { -} - -void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) { - _buffer = buffer; -} - -int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_buffer->getHeightBounds())) { - return STOP_RECURSION; - } - if (_depthFlags.size() > _depth) { - _depthFlags[_depth] = 0; - } else { - _depthFlags.append(0); - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - postVisit(info); - return STOP_RECURSION; -} - -bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { - HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - int flags = _depthFlags.at(_depth); - if (height) { - // to handle borders correctly, make sure we only sample nodes with resolution <= ours - int heightSize = glm::sqrt((float)height->getContents().size()); - float heightIncrement = info.size / heightSize; - if (heightIncrement < _buffer->getHeightIncrement() || (flags & HEIGHT_FLAG)) { - height.reset(); - } else { - flags |= HEIGHT_FLAG; - } - } - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color) { - int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); - float colorIncrement = info.size / colorSize; - if (colorIncrement < _buffer->getColorIncrement() || (flags & COLOR_FLAG)) { - color.reset(); - } else { - flags |= COLOR_FLAG; - } - } - HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - if (material) { - int materialSize = glm::sqrt((float)material->getContents().size()); - float materialIncrement = info.size / materialSize; - if (materialIncrement < _buffer->getMaterialIncrement() || (flags & MATERIAL_FLAG)) { - material.reset(); - } else { - flags |= MATERIAL_FLAG; - } - } - if (_depth > 0) { - _depthFlags[_depth - 1] |= flags; - } - if (!(height || color || material)) { - return false; - } - Box bounds = info.getBounds(); - foreach (const Box& intersection, _intersections) { - Box overlap = intersection.getIntersection(bounds); - if (overlap.isEmpty()) { - continue; - } - if (height) { - float heightIncrement = _buffer->getHeightIncrement(); - const Box& heightBounds = _buffer->getHeightBounds(); - int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; - int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); - int heightSize = _buffer->getHeightSize(); - char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - - const QByteArray& srcHeight = height->getContents(); - int srcSize = glm::sqrt((float)srcHeight.size()); - float srcIncrement = info.size / srcSize; - - if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - - const char* src = srcHeight.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { - memcpy(dest, src, destWidth); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = heightIncrement / srcIncrement; - int shift = 0; - float size = _buffer->getScale(); - while (size < info.size) { - shift++; - size *= 2.0f; - } - int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); - for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) { - const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); - } - } - } - } - if (color) { - const Box& colorBounds = _buffer->getColorBounds(); - overlap = colorBounds.getIntersection(overlap); - float colorIncrement = _buffer->getColorIncrement(); - int destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; - int destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); - int colorSize = _buffer->getColorSize(); - char* dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES; - int destStride = colorSize * DataBlock::COLOR_BYTES; - int destBytes = destWidth * DataBlock::COLOR_BYTES; - - const QByteArray& srcColor = color->getContents(); - int srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES)); - int srcStride = srcSize * DataBlock::COLOR_BYTES; - float srcIncrement = info.size / srcSize; - - if (srcIncrement == colorIncrement) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - - const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES; - for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { - memcpy(dest, src, destBytes); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = colorIncrement / srcIncrement; - for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) { - const char* src = srcColor.constData() + (int)srcY * srcStride; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES, - lineSrcX += srcAdvance) { - const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; - } - } - } - } - if (material) { - const Box& materialBounds = _buffer->getMaterialBounds(); - overlap = materialBounds.getIntersection(overlap); - float materialIncrement = _buffer->getMaterialIncrement(); - int destX = (overlap.minimum.x - materialBounds.minimum.x) / materialIncrement; - int destY = (overlap.minimum.z - materialBounds.minimum.z) / materialIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / materialIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / materialIncrement); - int materialSize = _buffer->getMaterialSize(); - char* dest = _buffer->getMaterial().data() + destY * materialSize + destX; - - const QByteArray& srcMaterial = material->getContents(); - const QVector srcMaterials = material->getMaterials(); - int srcSize = glm::sqrt((float)srcMaterial.size()); - float srcIncrement = info.size / srcSize; - QHash materialMappings; - - if (srcIncrement == materialIncrement) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - - const uchar* src = (const uchar*)srcMaterial.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += materialSize) { - const uchar* lineSrc = src; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrc++) { - int value = *lineSrc; - if (value != 0) { - int& mapping = materialMappings[value]; - if (mapping == 0) { - mapping = getMaterialIndex(material->getMaterials().at(value - 1), - _buffer->getMaterials(), _buffer->getMaterial()); - } - value = mapping; - } - *lineDest = value; - } - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = materialIncrement / srcIncrement; - for (int y = 0; y < destHeight; y++, dest += materialSize, srcY += srcAdvance) { - const uchar* src = (const uchar*)srcMaterial.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - int value = src[(int)lineSrcX]; - if (value != 0) { - int& mapping = materialMappings[value]; - if (mapping == 0) { - mapping = getMaterialIndex(material->getMaterials().at(value - 1), - _buffer->getMaterials(), _buffer->getMaterial()); - } - value = mapping; - } - *lineDest = value; - } - } - } - clearUnusedMaterials(_buffer->getMaterials(), _buffer->getMaterial()); - } - } - return false; -} - -class HeightfieldRegionVisitor : public MetavoxelVisitor { -public: - - QVector regions; - Box regionBounds; - - HeightfieldRegionVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); - virtual bool postVisit(MetavoxelInfo& info); - -private: - - void addRegion(const Box& unextended, const Box& extended); - - class DepthInfo { - public: - float minimumColorIncrement; - float minimumMaterialIncrement; - - DepthInfo() : minimumColorIncrement(FLT_MAX), minimumMaterialIncrement(FLT_MAX) { } - }; - - QVector _depthInfo; - - QVector _intersections; - HeightfieldFetchVisitor _fetchVisitor; -}; - -HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), - regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)), - _fetchVisitor(lod, _intersections) { -} - -int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { - DepthInfo depthInfo; - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color) { - int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); - depthInfo.minimumColorIncrement = info.size / colorSize; - } - HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - if (material) { - int materialSize = glm::sqrt((float)material->getContents().size()); - depthInfo.minimumMaterialIncrement = info.size / materialSize; - } - if (_depth < _depthInfo.size()) { - _depthInfo[_depth] = depthInfo; - } else { - _depthInfo.append(depthInfo); - } - if (!info.isLeaf) { - return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER; - } - postVisit(info); - return STOP_RECURSION; -} - -bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { - const DepthInfo& depthInfo = _depthInfo.at(_depth); - if (_depth > 0) { - DepthInfo& parentDepthInfo = _depthInfo[_depth - 1]; - parentDepthInfo.minimumColorIncrement = qMin(parentDepthInfo.minimumColorIncrement, depthInfo.minimumColorIncrement); - parentDepthInfo.minimumMaterialIncrement = qMin(parentDepthInfo.minimumMaterialIncrement, - depthInfo.minimumMaterialIncrement); - } - if (_visitations.at(_depth).isInputLeaf(0)) { - HeightfieldBuffer* buffer = NULL; - HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - if (height) { - int heightSize = glm::sqrt((float)height->getContents().size()); - int extendedHeightSize = heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int heightContentsSize = extendedHeightSize * extendedHeightSize; - float minimumColorIncrement = depthInfo.minimumColorIncrement; - float minimumMaterialIncrement = depthInfo.minimumMaterialIncrement; - for (int i = _depth - 1; i >= 0 && qMax(minimumColorIncrement, minimumMaterialIncrement) == FLT_MAX; i--) { - const DepthInfo& ancestorDepthInfo = _depthInfo.at(i); - minimumColorIncrement = qMin(minimumColorIncrement, ancestorDepthInfo.minimumColorIncrement); - minimumMaterialIncrement = qMin(minimumMaterialIncrement, ancestorDepthInfo.minimumMaterialIncrement); - } - int colorContentsSize = 0; - if (minimumColorIncrement != FLT_MAX) { - int colorSize = (int)glm::round(info.size / minimumColorIncrement) + HeightfieldBuffer::SHARED_EDGE; - colorContentsSize = colorSize * colorSize * DataBlock::COLOR_BYTES; - } - int materialContentsSize = 0; - if (minimumMaterialIncrement != FLT_MAX) { - int materialSize = (int)glm::round(info.size / minimumMaterialIncrement) + HeightfieldBuffer::SHARED_EDGE; - materialContentsSize = materialSize * materialSize; - } - const HeightfieldBuffer* existingBuffer = static_cast( - info.inputValues.at(3).getInlineValue().data()); - Box bounds = info.getBounds(); - if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize && - existingBuffer->getMaterial().size() == materialContentsSize) { - // we already have a buffer of the correct resolution - addRegion(bounds, existingBuffer->getHeightBounds()); - buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); - - } else { - // we must create a new buffer and update its borders - buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); - const Box& heightBounds = buffer->getHeightBounds(); - addRegion(bounds, heightBounds); - - _intersections.clear(); - _intersections.append(Box(heightBounds.minimum, - glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); - _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), - glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); - _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), - heightBounds.maximum)); - _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), - glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); - - _fetchVisitor.init(buffer); - _data->guide(_fetchVisitor); - } - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - } - return true; -} - -void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { - regions.append(unextended); - regionBounds.add(extended); -} - -class HeightfieldUpdateVisitor : public MetavoxelVisitor { -public: - - HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector& regions, const Box& regionBounds); - - virtual int visit(MetavoxelInfo& info); - -private: - - const QVector& _regions; - const Box& _regionBounds; - QVector _intersections; - HeightfieldFetchVisitor _fetchVisitor; -}; - -HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector& regions, - const Box& regionBounds) : - MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), - _regions(regions), - _regionBounds(regionBounds), - _fetchVisitor(lod, _intersections) { -} - -int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_regionBounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - const HeightfieldBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - _intersections.clear(); - foreach (const Box& region, _regions) { - if (region.intersects(buffer->getHeightBounds())) { - _intersections.append(region.getIntersection(buffer->getHeightBounds())); - } - } - if (_intersections.isEmpty()) { - return STOP_RECURSION; - } - HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, - buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials()); - _fetchVisitor.init(newBuffer); - _data->guide(_fetchVisitor); - BufferDataPointer pointer(newBuffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return STOP_RECURSION; -} - class VoxelAugmentVisitor : public MetavoxelVisitor { public: @@ -2392,27 +1596,13 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const while (expandedPrevious.getSize() < data.getSize()) { expandedPrevious.expand(); } - const AttributePointer& heightfieldBufferAttribute = - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(); - MetavoxelNode* root = expandedPrevious.getRoot(heightfieldBufferAttribute); - if (root) { - data.setRoot(heightfieldBufferAttribute, root); - root->incrementReferenceCount(); - } const AttributePointer& voxelBufferAttribute = Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); - root = expandedPrevious.getRoot(voxelBufferAttribute); + MetavoxelNode* root = expandedPrevious.getRoot(voxelBufferAttribute); if (root) { data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } - HeightfieldRegionVisitor heightfieldRegionVisitor(lod); - data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor); - - HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions, - heightfieldRegionVisitor.regionBounds); - data.guide(heightfieldUpdateVisitor); - VoxelAugmentVisitor voxelAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, voxelAugmentVisitor); } @@ -2535,19 +1725,6 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { - _baseHeightfieldProgram.bind(); - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); - data.guide(heightfieldRenderVisitor); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - _baseHeightfieldProgram.release(); - } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); @@ -2756,6 +1933,12 @@ void HeightfieldRenderer::init(Spanner* spanner) { connect(heightfield, &Heightfield::materialChanged, this, &HeightfieldRenderer::applyMaterial); } +class HeightfieldPoint { +public: + glm::vec3 vertex; + glm::vec2 textureCoord; +}; + void HeightfieldRenderer::render(bool cursor) { // create the buffer objects lazily Heightfield* heightfield = static_cast(_spanner); @@ -2765,8 +1948,8 @@ void HeightfieldRenderer::render(bool cursor) { int width = heightfield->getHeight()->getWidth(); int height = heightfield->getHeight()->getContents().size() / width; - int innerWidth = width - 2 * HeightfieldBuffer::HEIGHT_BORDER; - int innerHeight = height - 2 * HeightfieldBuffer::HEIGHT_BORDER; + int innerWidth = width - 2 * HeightfieldHeight::HEIGHT_BORDER; + int innerHeight = height - 2 * HeightfieldHeight::HEIGHT_BORDER; int vertexCount = width * height; int rows = height - 1; int columns = width - 1; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index a911db123a..90560e63aa 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -78,8 +78,6 @@ public: Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); - Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID); - signals: void rendering(); @@ -185,92 +183,6 @@ public: typedef QExplicitlySharedDataPointer BufferDataPointer; -/// Contains the information necessary to render a heightfield block. -class HeightfieldBuffer : public BufferData { -public: - - static const int HEIGHT_BORDER; - static const int SHARED_EDGE; - static const int HEIGHT_EXTENSION; - - HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, - const QByteArray& color, const QByteArray& material = QByteArray(), - const QVector& materials = QVector()); - ~HeightfieldBuffer(); - - const glm::vec3& getTranslation() const { return _translation; } - float getScale() const { return _scale; } - - const Box& getHeightBounds() const { return _heightBounds; } - const Box& getColorBounds() const { return _colorBounds; } - const Box& getMaterialBounds() const { return _materialBounds; } - - QByteArray& getHeight() { return _height; } - const QByteArray& getHeight() const { return _height; } - - QByteArray& getColor() { return _color; } - const QByteArray& getColor() const { return _color; } - - QByteArray& getMaterial() { return _material; } - const QByteArray& getMaterial() const { return _material; } - - QVector& getMaterials() { return _materials; } - const QVector& getMaterials() const { return _materials; } - - QByteArray getUnextendedHeight() const; - QByteArray getUnextendedColor(int x = 0, int y = 0) const; - - int getHeightSize() const { return _heightSize; } - float getHeightIncrement() const { return _heightIncrement; } - - int getColorSize() const { return _colorSize; } - float getColorIncrement() const { return _colorIncrement; } - - int getMaterialSize() const { return _materialSize; } - float getMaterialIncrement() const { return _materialIncrement; } - - virtual void render(bool cursor = false); - -private: - - glm::vec3 _translation; - float _scale; - Box _heightBounds; - Box _colorBounds; - Box _materialBounds; - QByteArray _height; - QByteArray _color; - QByteArray _material; - QVector _materials; - GLuint _heightTextureID; - GLuint _colorTextureID; - GLuint _materialTextureID; - QVector _networkTextures; - int _heightSize; - float _heightIncrement; - int _colorSize; - float _colorIncrement; - int _materialSize; - float _materialIncrement; - - 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; } - const QVector& getBuffers() const { return _buffers; } - - void render(const glm::vec3& translation, float scale) const; - -private: - - QVector _buffers; -}; - /// Describes contents of a vertex in a voxel buffer. class VoxelPoint { public: From f192f2428eb285d009ef4c7d75b1ac0b0b0a8e2b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Nov 2014 18:06:38 -0800 Subject: [PATCH 19/38] Height painting. --- .../metavoxels/src/MetavoxelMessages.cpp | 96 +++---------------- libraries/metavoxels/src/Spanner.cpp | 63 +++++++++++- libraries/metavoxels/src/Spanner.h | 12 ++- 3 files changed, 85 insertions(+), 86 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index c7471e7296..dc563d5c0e 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -154,87 +154,18 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position height(height) { } -class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor { -public: - - PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - PaintHeightfieldHeightEdit _edit; - Box _bounds; -}; - -PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), - QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute()), - _edit(edit) { - - glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius); - _bounds = Box(_edit.position - extents, _edit.position + extents); -} - -const int EIGHT_BIT_MAXIMUM = 255; - -int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue(); - if (!pointer) { - return STOP_RECURSION; - } - QByteArray contents(pointer->getContents()); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = size / info.size; - - glm::vec3 center = (_edit.position - info.minimum) * heightScale; - float scaledRadius = _edit.radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // raise/lower all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; - float squaredRadius = scaledRadius * scaledRadius; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size; - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = z - center.z; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - // height falls off towards edges - int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - if (value != *dest) { - *dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM); - changed = true; - } - } - } - lineDest += size; - } - if (changed) { - HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); - } - return STOP_RECURSION; -} - void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldHeightEditVisitor visitor(*this); - data.guide(visitor); + glm::vec3 extents(radius, radius, radius); + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), + Box(position - extents, position + extents), results); + + foreach (const SharedObjectPointer& spanner, results) { + Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, height); + if (newSpanner != spanner) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); + } + } } MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) : @@ -256,7 +187,7 @@ void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedOb Box(position - extents, position + extents), results); foreach (const SharedObjectPointer& spanner, results) { - Spanner* newSpanner = static_cast(spanner.data())->paint(position, radius, material, averageColor); + Spanner* newSpanner = static_cast(spanner.data())->paintMaterial(position, radius, material, averageColor); if (newSpanner != spanner) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); } @@ -419,7 +350,8 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { if (minZ > 0) { hermiteMinZ--; hermiteSizeZ++; - } + } + const int EIGHT_BIT_MAXIMUM = 255; QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples + hermiteMinX * VoxelHermiteData::EDGE_COUNT; for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) { diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 0808ae2ed1..325bd80d04 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -108,7 +108,12 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire return _bounds.findRayIntersection(origin, direction, distance); } -Spanner* Spanner::paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) { +Spanner* Spanner::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color) { + return this; +} + +Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height) { return this; } @@ -1608,7 +1613,7 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& return false; } -Spanner* Heightfield::paint(const glm::vec3& position, float radius, +Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) { if (!_height) { return this; @@ -1714,6 +1719,60 @@ Spanner* Heightfield::paint(const glm::vec3& position, float radius, return newHeightfield; } +Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + QVector contents = _height->getContents(); + int innerWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = heightWidth - 1; + int highestZ = heightHeight - 1; + Heightfield* newHeightfield = static_cast(clone(true)); + + glm::vec3 inverseScale(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ)); + glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + center.x += 1.0f; + center.z += 1.0f; + + glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; + glm::vec3 start = glm::floor(center - extents); + glm::vec3 end = glm::ceil(center + extents); + + // paint all points within the radius + float z = qMax(start.z, 0.0f); + float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + quint16* lineDest = contents.data() + (int)z * heightWidth + (int)startX; + float squaredRadius = extents.x * extents.x; + float squaredRadiusReciprocal = 1.0f / squaredRadius; + float scaledHeight = height * numeric_limits::max() / (getScale() * _aspectY); + float multiplierZ = inverseScale.x / inverseScale.z; + bool changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + quint16* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + float distanceSquared = dx * dx + dz * dz; + if (distanceSquared <= squaredRadius) { + // height falls off towards edges + int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + if (value != *dest) { + *dest = qMin(qMax(value, 0), (int)numeric_limits::max()); + changed = true; + } + } + } + lineDest += heightWidth; + } + if (changed) { + newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents))); + } + + return newHeightfield; +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index beaf6c8a7e..6e4e273726 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -66,7 +66,12 @@ public: /// Attempts to paint on the spanner. /// \return the modified spanner, or this if no modification was performed - virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color); + virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color); + + /// Attempts to modify the spanner's height. + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -515,8 +520,11 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color); + virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color); + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + signals: void aspectYChanged(float aspectY); From 683306797ddd6a2252abc5d1851b941e00452f88 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Nov 2014 11:43:16 -0800 Subject: [PATCH 20/38] Working on adjusting the range limits when we raise/lower past the original bounds. --- libraries/metavoxels/src/Spanner.cpp | 36 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 325bd80d04..ee397a1475 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1741,7 +1741,7 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float glm::vec3 start = glm::floor(center - extents); glm::vec3 end = glm::ceil(center + extents); - // paint all points within the radius + // first see if we're going to exceed the range limits float z = qMax(start.z, 0.0f); float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); quint16* lineDest = contents.data() + (int)z * heightWidth + (int)startX; @@ -1749,6 +1749,34 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float float squaredRadiusReciprocal = 1.0f / squaredRadius; float scaledHeight = height * numeric_limits::max() / (getScale() * _aspectY); float multiplierZ = inverseScale.x / inverseScale.z; + int minimumValue = 1, maximumValue = numeric_limits::max(); + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + quint16* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + float distanceSquared = dx * dx + dz * dz; + if (distanceSquared <= squaredRadius) { + // height falls off towards edges + int value = *dest; + if (value != 0) { + value += scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + minimumValue = qMin(minimumValue, value); + maximumValue = qMax(maximumValue, value); + } + } + } + lineDest += heightWidth; + } + + // renormalize if necessary + if (minimumValue < 1 || maximumValue > numeric_limits::max()) { + + } + + // now apply the actual change + z = qMax(start.z, 0.0f); + lineDest = contents.data() + (int)z * heightWidth + (int)startX; + scaledHeight = height * numeric_limits::max() / (getScale() * newHeightfield->getAspectY()); bool changed = false; for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { quint16* dest = lineDest; @@ -1757,9 +1785,9 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float float distanceSquared = dx * dx + dz * dz; if (distanceSquared <= squaredRadius) { // height falls off towards edges - int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - if (value != *dest) { - *dest = qMin(qMax(value, 0), (int)numeric_limits::max()); + int value = *dest; + if (value != 0) { + *dest = value + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; changed = true; } } From 2bb01d737c3bbd62b066b776d798cee1cc49afba Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Nov 2014 13:19:58 -0800 Subject: [PATCH 21/38] Renormalization. --- libraries/metavoxels/src/Spanner.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index ee397a1475..f6a5cf87bd 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1770,7 +1770,17 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float // renormalize if necessary if (minimumValue < 1 || maximumValue > numeric_limits::max()) { - + float scale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); + float offset = 1.0f - minimumValue; + newHeightfield->setAspectY(_aspectY / scale); + newHeightfield->setTranslation(getTranslation() - getRotation() * + glm::vec3(0.0f, offset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); + for (quint16* dest = contents.data(), *end = contents.data() + contents.size(); dest != end; dest++) { + int value = *dest; + if (value != 0) { + *dest = (value + offset) * scale; + } + } } // now apply the actual change From e946479b002063ea46fbcd80a49415ec13152fac Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Nov 2014 16:46:35 -0800 Subject: [PATCH 22/38] Working on heightfield voxelization. --- libraries/metavoxels/src/Spanner.cpp | 309 ++++++++++++++++++++++++++- libraries/metavoxels/src/Spanner.h | 11 +- 2 files changed, 314 insertions(+), 6 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f6a5cf87bd..bd0e2143cf 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1466,10 +1466,6 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& if (!_height) { return false; } - float boundsDistance; - if (!getBounds().findRayIntersection(origin, direction, boundsDistance)) { - return false; - } int width = _height->getWidth(); const QVector& contents = _height->getContents(); const quint16* src = contents.constData(); @@ -1483,7 +1479,14 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), innerHeight / (getScale() * _aspectZ)); glm::vec3 dir = inverseRotation * direction * inverseScale; - glm::vec3 entry = inverseRotation * (origin + direction * boundsDistance - getTranslation()) * inverseScale; + glm::vec3 entry = inverseRotation * (origin - getTranslation()) * inverseScale; + + float boundsDistance; + if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), + (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance)) { + return false; + } + entry += dir * boundsDistance; entry.x += HeightfieldHeight::HEIGHT_BORDER; entry.z += HeightfieldHeight::HEIGHT_BORDER; @@ -1811,6 +1814,302 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float return newHeightfield; } +bool Heightfield::hasOwnColors() const { + return _color; +} + +bool Heightfield::hasOwnMaterials() const { + return _material; +} + +QRgb Heightfield::getColorAt(const glm::vec3& point) { + int width = _color->getWidth(); + const QByteArray& contents = _color->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int height = contents.size() / (width * DataBlock::COLOR_BYTES); + int innerWidth = width - HeightfieldData::SHARED_EDGE; + int innerHeight = height - HeightfieldData::SHARED_EDGE; + + glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), + 1.0f, innerHeight / (getScale() * _aspectZ)); + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = (int)floors.x; + int floorZ = (int)floors.z; + int ceilX = (int)ceils.x; + int ceilZ = (int)ceils.z; + const uchar* upperLeft = src + (floorZ * width + floorX) * DataBlock::COLOR_BYTES; + const uchar* lowerRight = src + (ceilZ * width + ceilX) * DataBlock::COLOR_BYTES; + glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + const uchar* upperRight = src + (floorZ * width + ceilX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + const uchar* lowerLeft = src + (ceilZ * width + floorX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); + } + return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); +} + +int Heightfield::getMaterialAt(const glm::vec3& point) { + int width = _material->getWidth(); + const QByteArray& contents = _material->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldData::SHARED_EDGE; + int innerHeight = height - HeightfieldData::SHARED_EDGE; + + glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), + 1.0f, innerHeight / (getScale() * _aspectZ)); + return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; +} + +QVector& Heightfield::getMaterials() { + return _material->getMaterials(); +} + +bool Heightfield::contains(const glm::vec3& point) { + if (!_height) { + return false; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), + numeric_limits::max() / (getScale() * _aspectY), innerHeight / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.y < 0.0f || relative.z < 0.0f || relative.x > innerWidth || + relative.y > numeric_limits::max() || relative.z > innerHeight) { + return false; + } + relative.x += HeightfieldHeight::HEIGHT_BORDER; + relative.z += HeightfieldHeight::HEIGHT_BORDER; + + // find the bounds of the cell containing the point and the shared vertex heights + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + float upperRight = src[floorZ * width + ceilX]; + interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), + (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + float lowerLeft = src[ceilZ * width + floorX]; + interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); + } + if (interpolatedHeight == 0.0f) { + return false; // ignore zero values + } + + // compare + return relative.y <= interpolatedHeight; +} + +bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::quat inverseRotation = glm::inverse(getRotation()); + glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), + innerHeight / (getScale() * _aspectZ)); + glm::vec3 direction = end - start; + glm::vec3 dir = inverseRotation * direction * inverseScale; + glm::vec3 entry = inverseRotation * (start - getTranslation()) * inverseScale; + + float boundsDistance; + if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), + (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance) || boundsDistance > 1.0f) { + return false; + } + entry += dir * boundsDistance; + + const float DISTANCE_THRESHOLD = 0.001f; + if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) { + normal = glm::vec3(-1.0f, 0.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) { + normal = glm::vec3(1.0f, 0.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, -1.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.y - numeric_limits::max()) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, 1.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, 0.0f, -1.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) { + normal = glm::vec3(0.0f, 0.0f, 1.0f); + distance = boundsDistance; + return true; + } + + entry.x += HeightfieldHeight::HEIGHT_BORDER; + entry.z += HeightfieldHeight::HEIGHT_BORDER; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (dir.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (dir.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + + bool withinBounds = true; + float accumulatedDistance = boundsDistance; + while (withinBounds && accumulatedDistance <= 1.0f) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float upperRight = src[floorZ * width + ceilX]; + float lowerLeft = src[ceilZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (dir.x > 0.0f) { + xDistance = (ceils.x - entry.x) / dir.x; + } else if (dir.x < 0.0f) { + xDistance = (floors.x - entry.x) / dir.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (dir.z > 0.0f) { + zDistance = (ceils.z - entry.z) / dir.z; + } else if (dir.z < 0.0f) { + zDistance = (floors.z - entry.z) / dir.z; + } + + // the exit distance is the lower of those two + float exitDistance = qMin(xDistance, zDistance); + glm::vec3 exit, nextFloors = floors, nextCeils = ceils; + if (exitDistance == FLT_MAX) { + withinBounds = false; // line points upwards/downwards; check this cell only + + } else { + // find the exit point and the next cell, and determine whether it's still within the bounds + exit = entry + exitDistance * dir; + withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); + if (exitDistance == xDistance) { + if (dir.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highestX; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (dir.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highestZ; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.z -= 1.0f; + } + } + // check the vertical range of the ray against the ranges of the cell heights + if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || + qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + continue; + } + } + // having passed the bounds check, we must check against the planes + glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); + + // first check the triangle including the Z+ segment + glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); + float lowerProduct = glm::dot(lowerNormal, dir); + if (lowerProduct != 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + distance = accumulatedDistance + planeDistance; + normal = glm::normalize(getRotation() * (lowerNormal / inverseScale)); + return true; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, dir); + if (upperProduct != 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + distance = accumulatedDistance + planeDistance; + normal = glm::normalize(getRotation() * (upperNormal / inverseScale)); + return true; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return false; +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 6e4e273726..094524ab3e 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -465,7 +465,7 @@ public: HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference); const QByteArray& getContents() const { return _contents; } - const QVector& getMaterials() const { return _materials; } + QVector& getMaterials() { return _materials; } void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference); @@ -525,6 +525,15 @@ public: virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + virtual bool hasOwnColors() const; + virtual bool hasOwnMaterials() const; + virtual QRgb getColorAt(const glm::vec3& point); + virtual int getMaterialAt(const glm::vec3& point); + virtual QVector& getMaterials(); + + virtual bool contains(const glm::vec3& point); + virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); + signals: void aspectYChanged(float aspectY); From ba50beab911f486a4fa0995890837b15a31f22fc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Nov 2014 18:30:21 -0800 Subject: [PATCH 23/38] Normal fix. --- libraries/metavoxels/src/Spanner.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index bd0e2143cf..35b0754727 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2002,6 +2002,9 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float } } + glm::vec3 normalScale(1.0f / (inverseScale.y * inverseScale.z), 1.0f / (inverseScale.x * inverseScale.z), + 1.0f / (inverseScale.x * inverseScale.y)); + bool withinBounds = true; float accumulatedDistance = boundsDistance; while (withinBounds && accumulatedDistance <= 1.0f) { @@ -2081,7 +2084,7 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && intersection.z >= intersection.x) { distance = accumulatedDistance + planeDistance; - normal = glm::normalize(getRotation() * (lowerNormal / inverseScale)); + normal = glm::normalize(getRotation() * (lowerNormal * normalScale)); return true; } } @@ -2095,7 +2098,7 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && intersection.x >= intersection.z) { distance = accumulatedDistance + planeDistance; - normal = glm::normalize(getRotation() * (upperNormal / inverseScale)); + normal = glm::normalize(getRotation() * (upperNormal * normalScale)); return true; } } From a3e65fc231ca26cdcc465c38f12aac841fbbd20d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Nov 2014 18:44:38 -0800 Subject: [PATCH 24/38] Simplification, window fix. --- interface/src/MetavoxelSystem.cpp | 5 +---- interface/src/ui/MetavoxelEditor.cpp | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7edf96d1c6..28c2707049 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2036,12 +2036,9 @@ void HeightfieldRenderer::render(bool cursor) { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - float sDistance = 2.0f / (innerWidth - 1); - float tDistance = 2.0f / (innerHeight - 1); - float distanceProduct = -sDistance * tDistance; DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / width, 1.0f / height, - sDistance / distanceProduct, tDistance / distanceProduct); + (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)width / innerWidth, (float)height / innerHeight); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index f0634f5c40..43b542213d 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -48,7 +48,7 @@ enum GridPlane { const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); MetavoxelEditor::MetavoxelEditor() : - QWidget(Application::getInstance()->getGLWidget(), Qt::Tool) { + QWidget(Application::getInstance()->getWindow(), Qt::Tool) { setWindowTitle("Metavoxel Editor"); setAttribute(Qt::WA_DeleteOnClose); From a0f75c990e73eb38c36090560852c76aa97aeb83 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Nov 2014 17:12:47 -0800 Subject: [PATCH 25/38] Working on fetching/clearing heightfield chunks. --- interface/src/MetavoxelSystem.cpp | 11 + libraries/metavoxels/src/MetavoxelData.cpp | 4 + .../metavoxels/src/MetavoxelMessages.cpp | 292 ++---------------- libraries/metavoxels/src/Spanner.cpp | 169 +++++++++- libraries/metavoxels/src/Spanner.h | 7 + 5 files changed, 214 insertions(+), 269 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 28c2707049..db3ab28b8f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -363,6 +363,17 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); + + SpannerCursorRenderVisitor spannerVisitor(bounds); + guide(spannerVisitor); + + DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_POLYGON_OFFSET_FILL); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 94b3e0be97..535a336da7 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -344,6 +344,10 @@ int SpannerReplaceVisitor::visit(MetavoxelInfo& info) { void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) { Spanner* newSpanner = static_cast(newObject.data()); + if (!newSpanner) { + remove(attribute, bounds, granularity, oldObject); + return; + } if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) { // if the bounds have changed, we must remove and reinsert remove(attribute, bounds, granularity, oldObject); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index dc563d5c0e..a4d2569de0 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -155,7 +155,9 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position } void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - glm::vec3 extents(radius, radius, radius); + // increase the extents slightly to include neighboring tiles + const float RADIUS_EXTENSION = 1.1f; + glm::vec3 extents = glm::vec3(radius, radius, radius) * RADIUS_EXTENSION; QVector results; data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), Box(position - extents, position + extents), results); @@ -492,262 +494,6 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } -class HeightfieldClearFetchVisitor : public MetavoxelVisitor { -public: - - HeightfieldClearFetchVisitor(const Box& bounds, float granularity); - - const SharedObjectPointer& getSpanner() const { return _spanner; } - - virtual int visit(MetavoxelInfo& info); - -private: - - Box _bounds; - Box _expandedBounds; - SharedObjectPointer _spanner; - Box _spannerBounds; - int _heightfieldWidth; - int _heightfieldHeight; -}; - -HeightfieldClearFetchVisitor::HeightfieldClearFetchVisitor(const Box& bounds, float granularity) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()) { - - // find the bounds of all voxel nodes intersected - float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(glm::log(granularity) / glm::log(2.0f))); - _bounds.minimum = glm::floor(bounds.minimum / nodeSize) * nodeSize; - _bounds.maximum = glm::ceil(bounds.maximum / nodeSize) * nodeSize; - - // expand to include edges - _expandedBounds = _bounds; - float increment = nodeSize / VOXEL_BLOCK_SIZE; - _expandedBounds.maximum.x += increment; - _expandedBounds.maximum.z += increment; -} - -int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - if (!bounds.intersects(_expandedBounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldHeightDataPointer heightPointer = info.inputValues.at(0).getInlineValue(); - if (!heightPointer) { - return STOP_RECURSION; - } - QByteArray contents(heightPointer->getContents()); - int size = glm::sqrt((float)contents.size()); - float heightScale = size / info.size; - - Box overlap = bounds.getIntersection(_expandedBounds); - int srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - int srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - int srcWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale); - int srcHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* src = contents.data() + srcY * size + srcX; - - // check for non-zero values - bool foundNonZero = false; - for (int y = 0; y < srcHeight; y++, src += (size - srcWidth)) { - for (char* end = src + srcWidth; src != end; src++) { - if (*src != 0) { - foundNonZero = true; - goto outerBreak; - } - } - } - outerBreak: - - // if everything is zero, we're done - if (!foundNonZero) { - return STOP_RECURSION; - } - - // create spanner if necessary - TempHeightfield* spanner = static_cast(_spanner.data()); - float increment = 1.0f / heightScale; - if (!spanner) { - _spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment; - _spannerBounds.maximum = (glm::ceil(_bounds.maximum / increment) + glm::vec3(1.0f, 0.0f, 1.0f)) * increment; - _spannerBounds.minimum.y = bounds.minimum.y; - _spannerBounds.maximum.y = bounds.maximum.y; - _heightfieldWidth = (int)glm::round((_spannerBounds.maximum.x - _spannerBounds.minimum.x) / increment); - _heightfieldHeight = (int)glm::round((_spannerBounds.maximum.z - _spannerBounds.minimum.z) / increment); - int heightfieldArea = _heightfieldWidth * _heightfieldHeight; - Box innerBounds = _spannerBounds; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - _spanner = spanner = new TempHeightfield(innerBounds, increment, QByteArray(heightfieldArea, 0), - QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0), - QVector()); - } - - // copy the inner area - overlap = bounds.getIntersection(_spannerBounds); - int destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - int destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* dest = spanner->getHeight().data() + destY * _heightfieldWidth + destX; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - src = contents.data() + srcY * size + srcX; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { - memcpy(dest, src, destWidth); - } - - // clear the inner area - Box innerBounds = _spannerBounds; - innerBounds.minimum.x += increment; - innerBounds.minimum.z += increment; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - Box innerOverlap = bounds.getIntersection(innerBounds); - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - dest = contents.data() + destY * size + destX; - - for (int y = 0; y < destHeight; y++, dest += size) { - memset(dest, 0, destWidth); - } - - // see if there are any non-zero values left - foundNonZero = false; - dest = contents.data(); - for (char* end = dest + contents.size(); dest != end; dest++) { - if (*dest != 0) { - foundNonZero = true; - break; - } - } - - // if all is gone, clear the node - if (foundNonZero) { - HeightfieldHeightDataPointer newHeightPointer(new HeightfieldHeightData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newHeightPointer)); - - } else { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - } - - // allow a border for what we clear in terms of color/material - innerBounds.minimum.x += increment; - innerBounds.minimum.z += increment; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - innerOverlap = bounds.getIntersection(innerBounds); - - HeightfieldColorDataPointer colorPointer = info.inputValues.at(1).getInlineValue(); - if (colorPointer) { - contents = colorPointer->getContents(); - size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); - heightScale = size / info.size; - - // copy the inner area - destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - dest = spanner->getColor().data() + (destY * _heightfieldWidth + destX) * DataBlock::COLOR_BYTES; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - src = contents.data() + (srcY * size + srcX) * DataBlock::COLOR_BYTES; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth * DataBlock::COLOR_BYTES, - src += size * DataBlock::COLOR_BYTES) { - memcpy(dest, src, destWidth * DataBlock::COLOR_BYTES); - } - - if (foundNonZero) { - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - if (destWidth > 0 && destHeight > 0) { - dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES; - - for (int y = 0; y < destHeight; y++, dest += size * DataBlock::COLOR_BYTES) { - memset(dest, 0, destWidth * DataBlock::COLOR_BYTES); - } - - HeightfieldColorDataPointer newColorPointer(new HeightfieldColorData(contents)); - info.outputValues[1] = AttributeValue(_outputs.at(1), - encodeInline(newColorPointer)); - } - } else { - info.outputValues[1] = AttributeValue(_outputs.at(1)); - } - } - - HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); - if (materialPointer) { - contents = materialPointer->getContents(); - QVector materials = materialPointer->getMaterials(); - size = glm::sqrt((float)contents.size()); - heightScale = size / info.size; - - // copy the inner area - destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - uchar* dest = (uchar*)spanner->getMaterial().data() + destY * _heightfieldWidth + destX; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - uchar* src = (uchar*)contents.data() + srcY * size + srcX; - QHash materialMap; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { - for (uchar* lineSrc = src, *lineDest = dest, *end = src + destWidth; lineSrc != end; lineSrc++, lineDest++) { - int material = *lineSrc; - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(materials.at(material - 1), spanner->getMaterials(), - spanner->getMaterial()); - } - material = mapping; - } - *lineDest = material; - } - } - - if (foundNonZero) { - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - if (destWidth > 0 && destHeight > 0) { - dest = (uchar*)contents.data() + destY * size + destX; - - for (int y = 0; y < destHeight; y++, dest += size) { - memset(dest, 0, destWidth); - } - - clearUnusedMaterials(materials, contents); - HeightfieldMaterialDataPointer newMaterialPointer(new HeightfieldMaterialData(contents, materials)); - info.outputValues[2] = AttributeValue(_outputs.at(2), - encodeInline(newMaterialPointer)); - } - } else { - info.outputValues[2] = AttributeValue(_outputs.at(2)); - } - } - - return STOP_RECURSION; -} - void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit Spanner* spanner = static_cast(this->spanner.data()); @@ -758,14 +504,34 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject QColor color = averageColor; color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); - // clear/fetch any heightfield data - HeightfieldClearFetchVisitor heightfieldVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity()); - data.guide(heightfieldVisitor); + // find the bounds of all voxel nodes intersected + float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor( + glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f))); + Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize, + glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize); + + // expand to include edges + Box expandedBounds = bounds; + float increment = nodeSize / VOXEL_BLOCK_SIZE; + expandedBounds.maximum.x += increment; + expandedBounds.maximum.z += increment; + + // get all intersecting spanners + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results); + + // clear/voxelize as appropriate + SharedObjectPointer heightfield; + foreach (const SharedObjectPointer& result, results) { + Spanner* newSpanner = static_cast(result.data())->clearAndFetchHeight(bounds, heightfield); + if (newSpanner != result) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner); + } + } // voxelize the fetched heightfield, if any - if (heightfieldVisitor.getSpanner()) { - VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfieldVisitor.getSpanner().data()), - material, color); + if (heightfield) { + VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfield.data()), material, color); data.guide(visitor); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 35b0754727..3cd2b94e1b 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -117,6 +117,10 @@ Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float hei return this; } +Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { + return this; +} + bool Spanner::hasOwnColors() const { return false; } @@ -1814,6 +1818,159 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float return newHeightfield; } +Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + float heightIncrementX = getScale() / innerHeightWidth; + float heightIncrementZ = (getScale() * _aspectZ) / innerHeightHeight; + + int colorWidth = heightWidth; + int colorHeight = heightHeight; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + } + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + float colorIncrementX = getScale() / innerColorWidth; + float colorIncrementZ = (getScale() * _aspectZ) / innerColorHeight; + + int materialWidth = colorWidth; + int materialHeight = colorHeight; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + } + int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; + int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; + float materialIncrementX = getScale() / innerMaterialWidth; + float materialIncrementZ = (getScale() * _aspectZ) / innerMaterialHeight; + + float largestIncrementX = qMax(heightIncrementX, qMax(colorIncrementX, materialIncrementX)); + float largestIncrementZ = qMax(heightIncrementZ, qMax(colorIncrementZ, materialIncrementZ)); + + glm::vec3 minimum(glm::floor(bounds.minimum.x / largestIncrementX) * largestIncrementX, getBounds().minimum.y, + glm::floor(bounds.minimum.z / largestIncrementZ) * largestIncrementZ); + glm::vec3 maximum(glm::ceil(bounds.maximum.x / largestIncrementX) * largestIncrementX, getBounds().maximum.y, + glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ); + Box largestBounds(minimum, maximum); + + glm::mat4 baseTransform = glm::mat4_cast(glm::inverse(getRotation())) * glm::translate(-getTranslation()); + glm::vec3 inverseScale(innerHeightWidth / getScale(), 1.0f, innerHeightHeight / (getScale() * _aspectZ)); + glm::mat4 transform = glm::scale(inverseScale) * baseTransform; + Box transformedBounds = transform * largestBounds; + + // make sure there are values to clear + int startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightWidth - 1); + int startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightHeight - 1); + int endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, 0, heightWidth - 1); + int endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightHeight - 1); + const quint16* src = _height->getContents().constData() + startZ * heightWidth + startX; + for (int z = startZ; z <= endZ; z++, src += heightWidth) { + const quint16* lineSrc = src; + for (int x = startX; x <= endX; x++) { + if (*lineSrc++ != 0) { + goto clearableBreak; + } + } + } + return this; + clearableBreak: + + // create heightfield if necessary + Heightfield* spanner = static_cast(heightfield.data()); + if (!spanner) { + int spannerHeightWidth = (int)((maximum.x - minimum.x) / heightIncrementX) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerHeightHeight = (int)((maximum.z - minimum.z) / heightIncrementZ) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerColorWidth = (int)((maximum.x - bounds.minimum.x) / colorIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerColorHeight = (int)((maximum.z - minimum.z) / colorIncrementZ) + HeightfieldData::SHARED_EDGE; + int spannerMaterialWidth = (int)((maximum.x - minimum.x) / materialIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerMaterialHeight = (int)((maximum.z - minimum.z) / materialIncrementZ) + HeightfieldData::SHARED_EDGE; + + heightfield = spanner = new Heightfield(); + spanner->setTranslation(minimum); + spanner->setScale(maximum.x - minimum.x); + spanner->setAspectY((maximum.y - minimum.y) / spanner->getScale()); + spanner->setAspectZ((maximum.z - minimum.z) / spanner->getScale()); + spanner->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(spannerHeightWidth, + QVector(spannerHeightWidth * spannerHeightHeight)))); + spanner->setColor(HeightfieldColorPointer(new HeightfieldColor(spannerColorWidth, + QByteArray(spannerColorWidth * spannerColorHeight * DataBlock::COLOR_BYTES, 0xFF)))); + spanner->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(spannerMaterialWidth, + QByteArray(spannerMaterialWidth * spannerMaterialHeight, 0), QVector()))); + } + + // clear the height + QVector newHeightContents = _height->getContents(); + quint16* dest = newHeightContents.data() + startZ * heightWidth + startX; + for (int z = startZ; z <= endZ; z++, dest += heightWidth) { + memset(dest, 0, (endX - startX + 1) * sizeof(quint16)); + } + + // if we've cleared all the inner height, we can remove the spanner entirely + src = newHeightContents.constData() + heightWidth + HeightfieldHeight::HEIGHT_BORDER; + for (int z = 0; z < innerHeightHeight; z++, src += heightWidth) { + const quint16* lineSrc = src; + for (int x = 0; x < innerHeightWidth; x++) { + if (*lineSrc++ != 0) { + goto nonEmptyBreak; + } + } + } + return NULL; + nonEmptyBreak: + + Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents))); + + // and the color + if (_color) { + inverseScale = glm::vec3(innerColorWidth / getScale(), 1.0f, innerColorHeight / (getScale() * _aspectZ)); + transform = glm::scale(inverseScale) * baseTransform; + transformedBounds = transform * largestBounds; + startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, colorWidth - 1); + startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, colorHeight - 1); + endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, colorWidth - 1); + endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, colorHeight - 1); + QByteArray newColorContents = _color->getContents(); + char* dest = newColorContents.data() + (startZ * colorWidth + startX) * DataBlock::COLOR_BYTES; + for (int z = startZ; z <= endZ; z++, dest += colorWidth * DataBlock::COLOR_BYTES) { + memset(dest, 0, (endX - startX + 1) * DataBlock::COLOR_BYTES); + } + newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents))); + } + + // and the material + if (_material) { + inverseScale = glm::vec3(innerMaterialWidth / getScale(), 1.0f, innerMaterialHeight / (getScale() * _aspectZ)); + transform = glm::scale(inverseScale) * baseTransform; + transformedBounds = transform * largestBounds; + startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, materialWidth - 1); + startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, materialHeight - 1); + endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, materialWidth - 1); + endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, materialHeight - 1); + QByteArray newMaterialContents = _material->getContents(); + QVector newMaterials = _material->getMaterials(); + char* dest = newMaterialContents.data() + startZ * materialWidth + startX; + for (int z = startZ; z <= endZ; z++, dest += materialWidth) { + memset(dest, 0, endX - startX + 1); + } + clearUnusedMaterials(newMaterials, newMaterialContents); + newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial( + materialWidth, newMaterialContents, newMaterials))); + } + + return newHeightfield; +} + bool Heightfield::hasOwnColors() const { return _color; } @@ -1953,32 +2110,32 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float const float DISTANCE_THRESHOLD = 0.001f; if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) { - normal = glm::vec3(-1.0f, 0.0f, 0.0f); + normal = getRotation() * glm::vec3(-1.0f, 0.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) { - normal = glm::vec3(1.0f, 0.0f, 0.0f); + normal = getRotation() * glm::vec3(1.0f, 0.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, -1.0f, 0.0f); + normal = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.y - numeric_limits::max()) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 1.0f, 0.0f); + normal = getRotation() * glm::vec3(0.0f, 1.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, -1.0f); + normal = getRotation() * glm::vec3(0.0f, 0.0f, -1.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, 1.0f); + normal = getRotation() * glm::vec3(0.0f, 0.0f, 1.0f); distance = boundsDistance; return true; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 094524ab3e..45544f5377 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -73,6 +73,11 @@ public: /// \return the modified spanner, or this if no modification was performed virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + /// Attempts to clear and fetch part of the spanner's height. + /// \param heightfield the heightfield to populate + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -525,6 +530,8 @@ public: virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + virtual bool hasOwnColors() const; virtual bool hasOwnMaterials() const; virtual QRgb getColorAt(const glm::vec3& point); From 59fcff740b42d170b9761ba6814b9312078728ad Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Nov 2014 17:17:07 -0800 Subject: [PATCH 26/38] Remove remaining heightfield attribute bits. --- .../metavoxels/src/AttributeRegistry.cpp | 904 ------------------ libraries/metavoxels/src/AttributeRegistry.h | 137 --- 2 files changed, 1041 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index b90050f598..fbc7f0d110 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -21,9 +21,6 @@ REGISTER_META_OBJECT(FloatAttribute) REGISTER_META_OBJECT(MaterialObject) -REGISTER_META_OBJECT(HeightfieldAttribute) -REGISTER_META_OBJECT(HeightfieldColorAttribute) -REGISTER_META_OBJECT(HeightfieldMaterialAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -45,9 +42,6 @@ AttributeRegistry::AttributeRegistry() : _rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject, new DefaultMetavoxelRenderer()))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), - _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), - _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), - _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))), _voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))), _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))), _voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) { @@ -57,11 +51,6 @@ AttributeRegistry::AttributeRegistry() : _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); _spannersAttribute->setUserFacing(true); - const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; - _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f; _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); _voxelColorAttribute->setUserFacing(true); @@ -305,904 +294,11 @@ glm::vec3 unpackNormal(QRgb value) { DataBlock::~DataBlock() { } -enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; - -static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = false) { - if (image.isNull()) { - return QByteArray(1, NULL_HEIGHTFIELD_IMAGE); - } - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - const int JPEG_ENCODE_THRESHOLD = 16; - if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD && !lossless) { - qint32 offsetX = image.offset().x(), offsetY = image.offset().y(); - buffer.write((char*)&offsetX, sizeof(qint32)); - buffer.write((char*)&offsetY, sizeof(qint32)); - image.save(&buffer, "JPG"); - return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data()); - - } else { - buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE); - image.save(&buffer, "PNG"); - return buffer.data(); - } -} - -static QImage decodeHeightfieldImage(const QByteArray& data) { - switch (data.at(0)) { - case NULL_HEIGHTFIELD_IMAGE: - default: - return QImage(); - - case NORMAL_HEIGHTFIELD_IMAGE: - return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1)); - - case DEFLATED_HEIGHTFIELD_IMAGE: { - QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1); - const int OFFSET_SIZE = sizeof(qint32) * 2; - QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE); - const qint32* offsets = (const qint32*)inflated.constData(); - image.setOffset(QPoint(offsets[0], offsets[1])); - return image; - } - } -} - -HeightfieldHeightData::HeightfieldHeightData(const QByteArray& contents) : - _contents(contents) { -} - -HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); - if (image.isNull()) { - return; - } - QPoint offset = image.offset(); - image = image.convertToFormat(QImage::Format_RGB888); - if (offset.x() == 0) { - set(image); - return; - } - int minX = offset.x() - 1; - int minY = offset.y() - 1; - int size = glm::sqrt((float)_contents.size()); - char* lineDest = _contents.data() + minY * size + minX; - for (int y = 0; y < image.height(); y++) { - const uchar* src = image.constScanLine(y); - for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += COLOR_BYTES) { - *dest = *src; - } - lineDest += size; - } -} - -HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - subdivision.data = in.readAligned(bytes); - subdivision.ancestor = ancestor; - QImage image = decodeHeightfieldImage(subdivision.data); - if (image.isNull()) { - return; - } - image = image.convertToFormat(QImage::Format_RGB888); - int destSize = image.width(); - const uchar* src = image.constBits(); - const QByteArray& ancestorContents = ancestor->getContents(); - - int ancestorSize = glm::sqrt((float)ancestorContents.size()); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - - _contents = QByteArray(destSize * destSize, 0); - char* dest = _contents.data(); - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; - float ancestorX = minimum.x * ancestorSize; - for (char* end = dest + destSize; dest != end; src += COLOR_BYTES, ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX; - *dest++ = *ref++ + *src; - } - } -} - -void HeightfieldHeightData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - QImage image; - 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++) { - *dest++ = *src; - *dest++ = *src; - *dest++ = *src; - } - _encoded = encodeHeightfieldImage(image); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void HeightfieldHeightData::writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - QImage image; - int size = glm::sqrt((float)_contents.size()); - int minX = size, minY = size; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < size; y++) { - bool difference = false; - for (int x = 0; x < size; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (maxX >= minX) { - int width = qMax(maxX - minX + 1, 0); - int height = qMax(maxY - minY + 1, 0); - image = QImage(width, height, QImage::Format_RGB888); - const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX; - for (int y = 0; y < height; y++) { - uchar* dest = image.scanLine(y); - for (const uchar* src = lineSrc, *end = src + width; src != end; src++) { - *dest++ = *src; - *dest++ = *src; - *dest++ = *src; - } - lineSrc += size; - } - } - image.setOffset(QPoint(minX + 1, minY + 1)); - reference->setEncodedDelta(encodeHeightfieldImage(image)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void HeightfieldHeightData::writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - if (subdivision.data.isEmpty() || subdivision.ancestor != ancestor) { - QImage image; - const QByteArray& ancestorContents = ancestor->getContents(); - const uchar* src = (const uchar*)_contents.constData(); - - int destSize = glm::sqrt((float)_contents.size()); - image = QImage(destSize, destSize, QImage::Format_RGB888); - uchar* dest = image.bits(); - - int ancestorSize = glm::sqrt((float)ancestorContents.size()); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; - float ancestorX = minimum.x * ancestorSize; - for (const uchar* end = src + destSize; src != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX; - uchar difference = *src++ - *ref; - *dest++ = difference; - *dest++ = difference; - *dest++ = difference; - } - } - subdivision.data = encodeHeightfieldImage(image, true); - subdivision.ancestor = ancestor; - } - out << subdivision.data.size(); - out.writeAligned(subdivision.data); -} - -void HeightfieldHeightData::read(Bitstream& in, int bytes) { - set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888)); -} - -void HeightfieldHeightData::set(const QImage& image) { - _contents.resize(image.width() * image.height()); - char* dest = _contents.data(); - for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES; - src != end; src += COLOR_BYTES) { - *dest++ = *src; - } -} - -HeightfieldColorData::HeightfieldColorData(const QByteArray& contents) : - _contents(contents) { -} - -HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); - if (image.isNull()) { - return; - } - QPoint offset = image.offset(); - image = image.convertToFormat(QImage::Format_RGB888); - if (offset.x() == 0) { - set(image); - return; - } - int minX = offset.x() - 1; - int minY = offset.y() - 1; - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - char* dest = _contents.data() + (minY * size + minX) * COLOR_BYTES; - int destStride = size * COLOR_BYTES; - int srcStride = image.width() * COLOR_BYTES; - for (int y = 0; y < image.height(); y++) { - memcpy(dest, image.constScanLine(y), srcStride); - dest += destStride; - } -} - -HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - subdivision.data = in.readAligned(bytes); - subdivision.ancestor = ancestor; - QImage image = decodeHeightfieldImage(subdivision.data); - if (image.isNull()) { - return; - } - image = image.convertToFormat(QImage::Format_RGB888); - int destSize = image.width(); - const uchar* src = image.constBits(); - const QByteArray& ancestorContents = ancestor->getContents(); - - int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - int ancestorStride = ancestorSize * COLOR_BYTES; - - _contents = QByteArray(destSize * destSize * COLOR_BYTES, 0); - char* dest = _contents.data(); - int stride = image.width() * COLOR_BYTES; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; - float ancestorX = minimum.x * ancestorSize; - for (char* end = dest + stride; dest != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; - *dest++ = *ref++ + *src++; - *dest++ = *ref++ + *src++; - *dest++ = *ref++ + *src++; - } - } -} - -void HeightfieldColorData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - QImage image; - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); - _encoded = encodeHeightfieldImage(image); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void HeightfieldColorData::writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - QImage image; - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - int minX = size, minY = size; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < size; y++) { - bool difference = false; - for (int x = 0; x < size; x++, src += COLOR_BYTES, ref += COLOR_BYTES) { - if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (maxX >= minX) { - int width = maxX - minX + 1; - int height = maxY - minY + 1; - image = QImage(width, height, QImage::Format_RGB888); - src = _contents.constData() + (minY * size + minX) * COLOR_BYTES; - int srcStride = size * COLOR_BYTES; - int destStride = width * COLOR_BYTES; - for (int y = 0; y < height; y++) { - memcpy(image.scanLine(y), src, destStride); - src += srcStride; - } - } - image.setOffset(QPoint(minX + 1, minY + 1)); - reference->setEncodedDelta(encodeHeightfieldImage(image)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void HeightfieldColorData::writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - if (subdivision.data.isEmpty() || subdivision.ancestor != ancestor) { - QImage image; - const QByteArray& ancestorContents = ancestor->getContents(); - const uchar* src = (const uchar*)_contents.constData(); - - int destSize = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - image = QImage(destSize, destSize, QImage::Format_RGB888); - uchar* dest = image.bits(); - int stride = destSize * COLOR_BYTES; - - int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - int ancestorStride = ancestorSize * COLOR_BYTES; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; - float ancestorX = minimum.x * ancestorSize; - for (const uchar* end = src + stride; src != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; - *dest++ = *src++ - *ref++; - *dest++ = *src++ - *ref++; - *dest++ = *src++ - *ref++; - } - } - subdivision.data = encodeHeightfieldImage(image, true); - subdivision.ancestor = ancestor; - } - out << subdivision.data.size(); - out.writeAligned(subdivision.data); -} - -void HeightfieldColorData::read(Bitstream& in, int bytes) { - set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888)); -} - -void HeightfieldColorData::set(const QImage& image) { - _contents.resize(image.width() * image.height() * COLOR_BYTES); - memcpy(_contents.data(), image.constBits(), _contents.size()); -} - -const int HEIGHTFIELD_MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; - -static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_MATERIAL_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = width; - *header++ = height; - inflated.append(contents); - return qCompress(inflated); -} - -static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - width = *header++; - height = *header++; - return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); -} - -HeightfieldMaterialData::HeightfieldMaterialData(const QByteArray& contents, const QVector& materials) : - _contents(contents), - _materials(materials) { -} - -HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - in.readDelta(_materials, reference->getMaterials()); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - - int offsetX, offsetY, width, height; - QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - int size = glm::sqrt((float)_contents.size()); - const char* src = delta.constData(); - char* dest = _contents.data() + minY * size + minX; - for (int y = 0; y < height; y++, src += width, dest += size) { - memcpy(dest, src, width); - } -} - -void HeightfieldMaterialData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - int size = glm::sqrt((float)_contents.size()); - _encoded = encodeHeightfieldMaterial(0, 0, size, size, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); - out << _materials; -} - -void HeightfieldMaterialData::writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int size = glm::sqrt((float)_contents.size()); - int minX = size, minY = size; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < size; y++) { - bool difference = false; - for (int x = 0; x < size; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - QByteArray delta; - int width = 0, height = 0; - if (maxX >= minX) { - width = maxX - minX + 1; - height = maxY - minY + 1; - delta = QByteArray(width * height, 0); - char* dest = delta.data(); - src = _contents.constData() + minY * size + minX; - for (int y = 0; y < height; y++, src += size, dest += width) { - memcpy(dest, src, width); - } - } - reference->setEncodedDelta(encodeHeightfieldMaterial(minX + 1, minY + 1, width, height, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); - out.writeDelta(_materials, reference->getMaterials()); -} - -void HeightfieldMaterialData::read(Bitstream& in, int bytes) { - int offsetX, offsetY, width, height; - _contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); - in >> _materials; -} - MaterialObject::MaterialObject() : _scaleS(1.0f), _scaleT(1.0f) { } -HeightfieldAttribute::HeightfieldAttribute(const QString& name) : - InlineAttribute(name) { -} - -void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(); - } else { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData(in, size)); - } -} - -void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldHeightDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(); - } else { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData( - in, size, decodeInline(reference))); - } -} - -void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldHeightDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldHeightDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); - } - } - if (maxSize == 0) { - *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer(); - 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++) { - HeightfieldHeightDataPointer child = decodeInline(children[i]); - if (!child) { - continue; - } - const QByteArray& childContents = child->getContents(); - int childSize = glm::sqrt((float)childContents.size()); - 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* src = (uchar*)childContents.data(); - int childSizePlusOne = childSize + 1; - if (childSize == size) { - // simple case: one destination value for four child values - for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize; dest != end; src += 2) { - int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); - *dest++ = (max == 0) ? 0 : (yOffset + (max >> 1)); - } - dest += halfSize; - src += childSize; - } - } else { - // more complex: N destination values for four child values - int halfChildSize = childSize / 2; - int destPerSrc = size / childSize; - for (int z = 0; z < halfChildSize; z++) { - for (uchar* end = src + childSize; src != end; src += 2) { - int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); - memset(dest, (max == 0) ? 0 : (yOffset + (max >> 1)), destPerSrc); - dest += destPerSrc; - } - dest += halfSize; - for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - size, halfSize); - dest += size; - } - src += childSize; - } - } - } - *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer(new HeightfieldHeightData(contents)); - return false; -} - -AttributeValue HeightfieldAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : - InlineAttribute(name) { -} - -void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(); - } else { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData(in, size)); - } -} - -void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldColorDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(); - } else { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData( - in, size, decodeInline(reference))); - } -} - -void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldColorDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldColorDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); - } - } - if (maxSize == 0) { - *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(); - return true; - } - int size = glm::sqrt(maxSize / (float)DataBlock::COLOR_BYTES); - QByteArray contents(size * size * DataBlock::COLOR_BYTES, 0); - int halfSize = size / 2; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldColorDataPointer child = decodeInline(children[i]); - if (!child) { - continue; - } - const QByteArray& childContents = child->getContents(); - int childSize = glm::sqrt(childContents.size() / (float)DataBlock::COLOR_BYTES); - 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)) * DataBlock::COLOR_BYTES; - uchar* src = (uchar*)childContents.data(); - int childStride = childSize * DataBlock::COLOR_BYTES; - int stride = size * DataBlock::COLOR_BYTES; - int halfStride = stride / 2; - int childStep = 2 * DataBlock::COLOR_BYTES; - int redOffset3 = childStride + DataBlock::COLOR_BYTES; - int greenOffset1 = DataBlock::COLOR_BYTES + 1; - int greenOffset2 = childStride + 1; - int greenOffset3 = childStride + DataBlock::COLOR_BYTES + 1; - int blueOffset1 = DataBlock::COLOR_BYTES + 2; - int blueOffset2 = childStride + 2; - int blueOffset3 = childStride + DataBlock::COLOR_BYTES + 2; - if (childSize == size) { - // simple case: one destination value for four child values - for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize * DataBlock::COLOR_BYTES; dest != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[DataBlock::COLOR_BYTES] + - (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; - src += childStride; - } - } else { - // more complex: N destination values for four child values - int halfChildSize = childSize / 2; - int destPerSrc = size / childSize; - for (int z = 0; z < halfChildSize; z++) { - for (uchar* end = src + childSize * DataBlock::COLOR_BYTES; src != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[DataBlock::COLOR_BYTES] + - (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; - for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - DataBlock::COLOR_BYTES, DataBlock::COLOR_BYTES); - dest += DataBlock::COLOR_BYTES; - } - } - dest += halfStride; - for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - stride, halfStride); - dest += stride; - } - src += childStride; - } - } - } - *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(new HeightfieldColorData(contents)); - return false; -} - -AttributeValue HeightfieldColorAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) : - InlineAttribute(name) { -} - -void HeightfieldMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(); - } else { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(new HeightfieldMaterialData(in, size)); - } -} - -void HeightfieldMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldMaterialDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void HeightfieldMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(); - } else { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(new HeightfieldMaterialData( - in, size, decodeInline(reference))); - } -} - -void HeightfieldMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldMaterialDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldMaterialDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); - } - } - *(HeightfieldMaterialDataPointer*)&parent = HeightfieldMaterialDataPointer(); - return maxSize == 0; -} - -AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - static QHash countIndices(const QByteArray& contents) { QHash counts; for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 8df23bfae5..50814ac912 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -400,124 +400,6 @@ protected: QMutex _encodedSubdivisionsMutex; }; -typedef QExplicitlySharedDataPointer HeightfieldHeightDataPointer; - -/// Contains a block of heightfield height data. -class HeightfieldHeightData : public DataBlock { -public: - - HeightfieldHeightData(const QByteArray& contents); - HeightfieldHeightData(Bitstream& in, int bytes); - HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference); - HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size); - - const QByteArray& getContents() const { return _contents; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference); - void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size); - -private: - - void read(Bitstream& in, int bytes); - void set(const QImage& image); - - QByteArray _contents; -}; - -/// An attribute that stores heightfield data. -class HeightfieldAttribute : public InlineAttribute { - Q_OBJECT - -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 void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - -typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; - -/// Contains a block of heightfield color data. -class HeightfieldColorData : public DataBlock { -public: - - HeightfieldColorData(const QByteArray& contents); - HeightfieldColorData(Bitstream& in, int bytes); - HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference); - HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size); - - const QByteArray& getContents() const { return _contents; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference); - void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size); - -private: - - void read(Bitstream& in, int bytes); - void set(const QImage& image); - - QByteArray _contents; -}; - -/// An attribute that stores heightfield colors. -class HeightfieldColorAttribute : public InlineAttribute { - Q_OBJECT - -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 void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - -typedef QExplicitlySharedDataPointer HeightfieldMaterialDataPointer; - -/// Contains a block of heightfield material data. -class HeightfieldMaterialData : public DataBlock { -public: - - HeightfieldMaterialData(const QByteArray& contents, - const QVector& materials = QVector()); - HeightfieldMaterialData(Bitstream& in, int bytes); - HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference); - - const QByteArray& getContents() const { return _contents; } - - const QVector& getMaterials() const { return _materials; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QByteArray _contents; - QVector _materials; -}; - /// Contains the description of a material. class MaterialObject : public SharedObject { Q_OBJECT @@ -541,25 +423,6 @@ private: float _scaleT; }; -/// An attribute that stores heightfield materials. -class HeightfieldMaterialAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE HeightfieldMaterialAttribute(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 void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - /// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, /// creating a new entry in the list if necessary. uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); From 0cc92dc87e519530ba3854e4ae708fbcc5fb55d5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Nov 2014 19:15:06 -0800 Subject: [PATCH 27/38] More spanner voxelization bits. --- libraries/metavoxels/src/Spanner.cpp | 405 +++++++++------------------ libraries/metavoxels/src/Spanner.h | 39 +-- 2 files changed, 128 insertions(+), 316 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 3cd2b94e1b..617d753414 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -359,268 +359,6 @@ QByteArray StaticModel::getRendererClassName() const { return "StaticModelRenderer"; } -const float EIGHT_BIT_MAXIMUM = 255.0f; - -TempHeightfield::TempHeightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color, - const QByteArray& material, const QVector& materials) : - _increment(increment), - _width((int)glm::round((bounds.maximum.x - bounds.minimum.x) / increment) + 1), - _heightScale((bounds.maximum.y - bounds.minimum.y) / EIGHT_BIT_MAXIMUM), - _height(height), - _color(color), - _material(material), - _materials(materials) { - - setBounds(bounds); -} - -bool TempHeightfield::hasOwnColors() const { - return true; -} - -bool TempHeightfield::hasOwnMaterials() const { - return true; -} - -QRgb TempHeightfield::getColorAt(const glm::vec3& point) { - glm::vec3 relative = (point - getBounds().minimum) / _increment; - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - const uchar* src = (const uchar*)_color.constData(); - const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES; - const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES; - glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); - } - return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); -} - -int TempHeightfield::getMaterialAt(const glm::vec3& point) { - glm::vec3 relative = (point - getBounds().minimum) / _increment; - const uchar* src = (const uchar*)_material.constData(); - return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)]; -} - -QVector& TempHeightfield::getMaterials() { - return _materials; -} - -bool TempHeightfield::contains(const glm::vec3& point) { - if (!getBounds().contains(point)) { - return false; - } - glm::vec3 relative = (point - getBounds().minimum) / _increment; - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - const uchar* src = (const uchar*)_height.constData(); - float upperLeft = src[floorZ * _width + floorX]; - float lowerRight = src[ceilZ * _width + ceilX]; - float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - float upperRight = src[floorZ * _width + ceilX]; - interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), - (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - float lowerLeft = src[ceilZ * _width + floorX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); - } - return interpolatedHeight != 0.0f && point.y <= interpolatedHeight * _heightScale + getBounds().minimum.y; -} - -bool TempHeightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - // find the initial location in heightfield coordinates - float rayDistance; - glm::vec3 direction = end - start; - if (!getBounds().findRayIntersection(start, direction, rayDistance) || rayDistance > 1.0f) { - return false; - } - glm::vec3 entry = start + direction * rayDistance; - const float DISTANCE_THRESHOLD = 0.001f; - if (glm::abs(entry.x - getBounds().minimum.x) < DISTANCE_THRESHOLD) { - normal = glm::vec3(-1.0f, 0.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.x - getBounds().maximum.x) < DISTANCE_THRESHOLD) { - normal = glm::vec3(1.0f, 0.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.y - getBounds().minimum.y) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, -1.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.y - getBounds().maximum.y) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 1.0f, 0.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.z - getBounds().minimum.z) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, -1.0f); - distance = rayDistance; - return true; - - } else if (glm::abs(entry.z - getBounds().maximum.z) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, 1.0f); - distance = rayDistance; - return true; - } - entry = (entry - getBounds().minimum) / _increment; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (direction.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (direction.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - bool withinBounds = true; - float accumulatedDistance = 0.0f; - const uchar* src = (const uchar*)_height.constData(); - int highestX = _width - 1; - float highestY = (getBounds().maximum.y - getBounds().minimum.y) / _increment; - int highestZ = (int)glm::round((getBounds().maximum.z - getBounds().minimum.z) / _increment); - float heightScale = _heightScale / _increment; - while (withinBounds && accumulatedDistance <= 1.0f) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, 0), highestX); - int floorZ = qMin(qMax((int)floors.z, 0), highestZ); - int ceilX = qMin(qMax((int)ceils.x, 0), highestX); - int ceilZ = qMin(qMax((int)ceils.z, 0), highestZ); - float upperLeft = src[floorZ * _width + floorX] * heightScale; - float upperRight = src[floorZ * _width + ceilX] * heightScale; - float lowerLeft = src[ceilZ * _width + floorX] * heightScale; - float lowerRight = src[ceilZ * _width + ceilX] * heightScale; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (direction.x > 0.0f) { - xDistance = (ceils.x - entry.x) / direction.x; - } else if (direction.x < 0.0f) { - xDistance = (floors.x - entry.x) / direction.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (direction.z > 0.0f) { - zDistance = (ceils.z - entry.z) / direction.z; - } else if (direction.z < 0.0f) { - zDistance = (floors.z - entry.z) / direction.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - withinBounds = false; // line points upwards/downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * direction; - withinBounds = (exit.y >= 0.0f && exit.y <= highestY); - if (exitDistance == xDistance) { - if (direction.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highestX; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (direction.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highestZ; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, direction); - if (lowerProduct != 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - distance = rayDistance + (accumulatedDistance + planeDistance) * _increment; - normal = glm::normalize(lowerNormal); - return true; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, direction); - if (upperProduct != 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * direction; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - distance = rayDistance + (accumulatedDistance + planeDistance) * _increment; - normal = glm::normalize(upperNormal); - return true; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return false; -} - const int HeightfieldData::SHARED_EDGE = 1; HeightfieldData::HeightfieldData(int width) : @@ -914,7 +652,7 @@ void HeightfieldHeightEditor::select() { int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER; QVector contents(width * height); quint16* dest = contents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; - const float CONVERSION_SCALE = 65534.0f / EIGHT_BIT_MAXIMUM; + const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); for (int i = 0; i < image.height(); i++, dest += width) { const uchar* src = image.constScanLine(i); for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++, @@ -1425,25 +1163,23 @@ float Heightfield::getHeight(const glm::vec3& location) const { int height = contents.size() / width; int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; - int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; glm::vec3 relative = glm::inverse(getRotation()) * (location - getTranslation()) * glm::vec3(1.0f / getScale(), 1.0f, 1.0f / (getScale() * _aspectZ)); - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > 1.0f || relative.z > 1.0f) { - return -FLT_MAX; - } relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER; relative.z = relative.z * innerHeight + HeightfieldHeight::HEIGHT_BORDER; + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return -FLT_MAX; + } // find the bounds of the cell containing the point and the shared vertex heights glm::vec3 floors = glm::floor(relative); glm::vec3 ceils = glm::ceil(relative); glm::vec3 fracts = glm::fract(relative); - int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int floorX = (int)floors.x; + int floorZ = (int)floors.z; + int ceilX = (int)ceils.x; + int ceilZ = (int)ceils.z; float upperLeft = src[floorZ * width + floorX]; float lowerRight = src[ceilZ * width + ceilX]; float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); @@ -1860,6 +1596,12 @@ Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ); Box largestBounds(minimum, maximum); + // enlarge the area to fetch + minimum.x -= largestIncrementX; + maximum.x += largestIncrementX; + minimum.z -= largestIncrementZ; + maximum.z += largestIncrementX; + glm::mat4 baseTransform = glm::mat4_cast(glm::inverse(getRotation())) * glm::translate(-getTranslation()); glm::vec3 inverseScale(innerHeightWidth / getScale(), 1.0f, innerHeightHeight / (getScale() * _aspectZ)); glm::mat4 transform = glm::scale(inverseScale) * baseTransform; @@ -1885,16 +1627,16 @@ Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer return this; clearableBreak: + int spannerHeightWidth = (int)((maximum.x - minimum.x) / heightIncrementX) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerHeightHeight = (int)((maximum.z - minimum.z) / heightIncrementZ) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerColorWidth = (int)((maximum.x - minimum.x) / colorIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerColorHeight = (int)((maximum.z - minimum.z) / colorIncrementZ) + HeightfieldData::SHARED_EDGE; + int spannerMaterialWidth = (int)((maximum.x - minimum.x) / materialIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerMaterialHeight = (int)((maximum.z - minimum.z) / materialIncrementZ) + HeightfieldData::SHARED_EDGE; + // create heightfield if necessary Heightfield* spanner = static_cast(heightfield.data()); if (!spanner) { - int spannerHeightWidth = (int)((maximum.x - minimum.x) / heightIncrementX) + HeightfieldHeight::HEIGHT_EXTENSION; - int spannerHeightHeight = (int)((maximum.z - minimum.z) / heightIncrementZ) + HeightfieldHeight::HEIGHT_EXTENSION; - int spannerColorWidth = (int)((maximum.x - bounds.minimum.x) / colorIncrementX) + HeightfieldData::SHARED_EDGE; - int spannerColorHeight = (int)((maximum.z - minimum.z) / colorIncrementZ) + HeightfieldData::SHARED_EDGE; - int spannerMaterialWidth = (int)((maximum.x - minimum.x) / materialIncrementX) + HeightfieldData::SHARED_EDGE; - int spannerMaterialHeight = (int)((maximum.z - minimum.z) / materialIncrementZ) + HeightfieldData::SHARED_EDGE; - heightfield = spanner = new Heightfield(); spanner->setTranslation(minimum); spanner->setScale(maximum.x - minimum.x); @@ -1908,9 +1650,106 @@ Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer QByteArray(spannerMaterialWidth * spannerMaterialHeight, 0), QVector()))); } + // fetch the height + glm::vec3 spannerInverseScale((spannerHeightWidth - HeightfieldHeight::HEIGHT_EXTENSION) / spanner->getScale(), 1.0f, + (spannerHeightHeight - HeightfieldHeight::HEIGHT_EXTENSION) / (spanner->getScale() * spanner->getAspectZ())); + glm::mat4 spannerBaseTransform = glm::translate(-spanner->getTranslation()); + glm::mat4 spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; + Box spannerTransformedBounds = spannerTransform * getBounds(); + int spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightWidth - 1); + int spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightHeight - 1); + int spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightWidth - 1); + int spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightHeight - 1); + quint16* dest = spanner->getHeight()->getContents().data() + spannerStartZ * spannerHeightWidth + spannerStartX; + glm::vec3 step = 1.0f / spannerInverseScale; + glm::vec3 initialPosition = glm::vec3(spannerStartX - HeightfieldHeight::HEIGHT_BORDER, 0, + spannerStartZ - HeightfieldHeight::HEIGHT_BORDER) * step + spanner->getTranslation(); + glm::vec3 position = initialPosition; + float heightScale = numeric_limits::max() / (getScale() * _aspectY); + for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerHeightWidth, position.z += step.z) { + quint16* lineDest = dest; + position.x = initialPosition.x; + for (int x = spannerStartX; x <= spannerEndX; x++, lineDest++, position.x += step.x) { + float height = (getHeight(position) - getTranslation().y) * heightScale; + if (height > *lineDest) { + *lineDest = height; + } + } + } + + // and the color + if (_color) { + spannerInverseScale = glm::vec3((spannerColorWidth - HeightfieldData::SHARED_EDGE) / spanner->getScale(), 1.0f, + (spannerColorHeight - HeightfieldData::SHARED_EDGE) / (spanner->getScale() * spanner->getAspectZ())); + spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; + spannerTransformedBounds = spannerTransform * getBounds(); + spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x), 0, spannerColorWidth - 1); + spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z), 0, spannerColorHeight - 1); + spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x), 0, spannerColorWidth - 1); + spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z), 0, spannerColorHeight - 1); + + char* dest = spanner->getColor()->getContents().data() + + (spannerStartZ * spannerColorWidth + spannerStartX) * DataBlock::COLOR_BYTES; + step = 1.0f / spannerInverseScale; + initialPosition = glm::vec3(spannerStartX, 0, spannerStartZ) * step + spanner->getTranslation(); + position = initialPosition; + for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerColorWidth * DataBlock::COLOR_BYTES, + position.z += step.z) { + char* lineDest = dest; + position.x = initialPosition.x; + for (int x = spannerStartX; x <= spannerEndX; x++, lineDest += DataBlock::COLOR_BYTES, position.x += step.x) { + QRgb color = getColorAt(position); + if (color != 0) { + lineDest[0] = qRed(color); + lineDest[1] = qGreen(color); + lineDest[2] = qBlue(color); + } + } + } + } + + // and the material + if (_material) { + spannerInverseScale = glm::vec3((spannerMaterialWidth - HeightfieldData::SHARED_EDGE) / spanner->getScale(), 1.0f, + (spannerMaterialHeight - HeightfieldData::SHARED_EDGE) / (spanner->getScale() * spanner->getAspectZ())); + spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; + spannerTransformedBounds = spannerTransform * getBounds(); + spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x), 0, spannerMaterialWidth - 1); + spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z), 0, spannerMaterialHeight - 1); + spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x), 0, spannerMaterialWidth - 1); + spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z), 0, spannerMaterialHeight - 1); + + char* dest = spanner->getMaterial()->getContents().data() + spannerStartZ * spannerMaterialWidth + spannerStartX; + step = 1.0f / spannerInverseScale; + initialPosition = glm::vec3(spannerStartX, 0, spannerStartZ) * step + spanner->getTranslation(); + position = initialPosition; + QHash materialMap; + for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerMaterialWidth, position.z += step.z) { + char* lineDest = dest; + position.x = initialPosition.x; + for (int x = spannerStartX; x <= spannerEndX; x++, lineDest++, position.x += step.x) { + int material = getMaterialAt(position); + if (material != -1) { + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + material = mapping = getMaterialIndex(_material->getMaterials().at(material - 1), + spanner->getMaterial()->getMaterials(), spanner->getMaterial()->getContents()); + } + } + *lineDest = material; + } + } + } + } + // clear the height QVector newHeightContents = _height->getContents(); - quint16* dest = newHeightContents.data() + startZ * heightWidth + startX; + dest = newHeightContents.data() + startZ * heightWidth + startX; for (int z = startZ; z <= endZ; z++, dest += heightWidth) { memset(dest, 0, (endX - startX + 1) * sizeof(quint16)); } @@ -1989,6 +1828,9 @@ QRgb Heightfield::getColorAt(const glm::vec3& point) { glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return 0; + } glm::vec3 floors = glm::floor(relative); glm::vec3 ceils = glm::ceil(relative); glm::vec3 fracts = glm::fract(relative); @@ -2025,6 +1867,9 @@ int Heightfield::getMaterialAt(const glm::vec3& point) { glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return -1; + } return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 45544f5377..7fe32b56a6 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -284,39 +284,6 @@ private: QUrl _url; }; -/// A heightfield represented as a spanner. -class TempHeightfield : public Transformable { - Q_OBJECT - -public: - - TempHeightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color, - const QByteArray& material, const QVector& materials); - - QByteArray& getHeight() { return _height; } - QByteArray& getColor() { return _color; } - QByteArray& getMaterial() { return _material; } - - virtual bool hasOwnColors() const; - virtual bool hasOwnMaterials() const; - virtual QRgb getColorAt(const glm::vec3& point); - virtual int getMaterialAt(const glm::vec3& point); - virtual QVector& getMaterials(); - - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -private: - - float _increment; - int _width; - float _heightScale; - QByteArray _height; - QByteArray _color; - QByteArray _material; - QVector _materials; -}; - /// Base class for heightfield data blocks. class HeightfieldData : public DataBlock { public: @@ -345,7 +312,7 @@ public: HeightfieldHeight(Bitstream& in, int bytes); HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference); - const QVector& getContents() const { return _contents; } + QVector& getContents() { return _contents; } void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference); @@ -407,7 +374,7 @@ public: HeightfieldColor(Bitstream& in, int bytes); HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference); - const QByteArray& getContents() const { return _contents; } + QByteArray& getContents() { return _contents; } void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldColorPointer& reference); @@ -469,7 +436,7 @@ public: HeightfieldMaterial(Bitstream& in, int bytes); HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference); - const QByteArray& getContents() const { return _contents; } + QByteArray& getContents() { return _contents; } QVector& getMaterials() { return _materials; } void write(Bitstream& out); From d273e7c3993683a38322b985b934f0dd44b0959e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Nov 2014 19:49:08 -0800 Subject: [PATCH 28/38] Render heightfields -> render spanners. --- interface/src/Menu.cpp | 2 +- interface/src/Menu.h | 2 +- interface/src/MetavoxelSystem.cpp | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f7aabc8f06..dbafae490e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -462,7 +462,7 @@ Menu::Menu() : QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true); + addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true); addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, SLOT(showMetavoxelNetworkSimulator())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 8590d8580e..2dfadaa85d 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -451,9 +451,9 @@ namespace MenuOption { const QString RenderEntitiesAsScene = "Render Entities as Scene"; const QString RenderFocusIndicator = "Show Eye Focus"; const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; - const QString RenderHeightfields = "Render Heightfields"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; + const QString RenderSpanners = "Render Spanners"; const QString RenderTargetFramerate = "Framerate"; const QString RenderTargetFramerateUnlimited = "Unlimited"; const QString RenderTargetFramerate60 = "60"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index db3ab28b8f..8964571e5f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1723,8 +1723,10 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) { } void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { - SpannerRenderVisitor spannerRenderVisitor(lod); - data.guide(spannerRenderVisitor); + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSpanners)) { + SpannerRenderVisitor spannerRenderVisitor(lod); + data.guide(spannerRenderVisitor); + } Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); @@ -1953,7 +1955,7 @@ public: void HeightfieldRenderer::render(bool cursor) { // create the buffer objects lazily Heightfield* heightfield = static_cast(_spanner); - if (!heightfield->getHeight() || !Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { + if (!heightfield->getHeight()) { return; } int width = heightfield->getHeight()->getWidth(); From 82fc75b2c4e3244985d279aeaf88cc2a9c092b52 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Nov 2014 17:27:10 -0800 Subject: [PATCH 29/38] Tweak to normal computation on dual contour surfaces. --- interface/src/MetavoxelSystem.cpp | 200 ++++++++++++++---------------- 1 file changed, 91 insertions(+), 109 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 8964571e5f..9ab06f1006 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1020,16 +1020,37 @@ public: glm::vec3 normal; QRgb color; char material; - int axis; }; -class AxisIndex { +const int MAX_NORMALS_PER_VERTEX = 4; + +class NormalIndex { public: - int x, y, z; + int indices[MAX_NORMALS_PER_VERTEX]; - AxisIndex(int x = -1, int y = -1, int z = -1) : x(x), y(y), z(z) { } + int getClosestIndex(const glm::vec3& normal, QVector& vertices) const; }; +int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector& vertices) const { + int firstIndex = indices[0]; + int closestIndex = firstIndex; + const VoxelPoint& firstVertex = vertices.at(firstIndex); + float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; + for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { + int index = indices[i]; + if (index == firstIndex) { + break; + } + const VoxelPoint& vertex = vertices.at(index); + float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; + if (product > closest) { + closest = product; + closestIndex = index; + } + } + return closestIndex; +} + static glm::vec3 safeNormalize(const glm::vec3& vector) { float length = glm::length(vector); return (length > 0.0f) ? (vector / length) : vector; @@ -1075,10 +1096,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { // as we scan down the cube generating vertices between grid points, we remember the indices of the last // (element, line, section--x, y, z) so that we can connect generated vertices as quads int expanded = size + 1; - QVector lineIndices(expanded); - QVector lastLineIndices(expanded); - QVector planeIndices(expanded * expanded); - QVector lastPlaneIndices(expanded * expanded); + QVector lineIndices(expanded); + QVector lastLineIndices(expanded); + QVector planeIndices(expanded * expanded); + QVector lastPlaneIndices(expanded * expanded); const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; @@ -1090,7 +1111,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { for (int z = 0; z < expanded; z++) { const QRgb* colorY = colorZ; for (int y = 0; y < expanded; y++) { - AxisIndex lastIndex; + NormalIndex lastIndex; const QRgb* colorX = colorY; for (int x = 0; x < expanded; x++) { int alpha0 = colorX[0] >> ALPHA_OFFSET; @@ -1165,7 +1186,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 0.0f); - crossing.axis = 0; } if (middleY) { if (alpha1 != alpha3) { @@ -1180,7 +1200,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); - crossing.axis = 1; } if (alpha2 != alpha3) { QRgb hermite = hermiteBase[hermiteStride]; @@ -1194,7 +1213,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 0.0f); - crossing.axis = 0; } if (middleZ) { if (alpha3 != alpha7) { @@ -1209,7 +1227,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[offset3] : 0; } crossing.point = glm::vec3(1.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } if (alpha5 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + VoxelHermiteData::EDGE_COUNT + 1]; @@ -1223,7 +1240,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[offset5] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); - crossing.axis = 1; } if (alpha6 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + hermiteStride]; @@ -1237,7 +1253,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[offset6] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 1.0f); - crossing.axis = 0; } } } @@ -1254,7 +1269,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } if (alpha4 != alpha5) { QRgb hermite = hermiteBase[hermiteArea]; @@ -1268,7 +1282,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 1.0f); - crossing.axis = 0; } } } @@ -1285,7 +1298,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); - crossing.axis = 1; } if (middleZ) { if (alpha2 != alpha6) { @@ -1300,7 +1312,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(0.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } if (alpha4 != alpha6) { QRgb hermite = hermiteBase[hermiteArea + 1]; @@ -1314,7 +1325,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); - crossing.axis = 1; } } } @@ -1330,12 +1340,13 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } // at present, we simply average the properties of each crossing as opposed to finding the vertex that // minimizes the quadratic error function as described in the reference paper glm::vec3 center; - glm::vec3 axisNormals[3]; + glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; + int normalCount = 0; + const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); const int MAX_MATERIALS_PER_VERTEX = 4; quint8 materials[] = { 0, 0, 0, 0 }; glm::vec4 materialWeights; @@ -1344,7 +1355,18 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; center += crossing.point; - axisNormals[crossing.axis] += crossing.normal; + + int j = 0; + for (; j < normalCount; j++) { + if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { + normals[j] = safeNormalize(normals[j] + crossing.normal); + break; + } + } + if (j == normalCount) { + normals[normalCount++] = crossing.normal; + } + red += qRed(crossing.color); green += qGreen(crossing.color); blue += qBlue(crossing.color); @@ -1359,7 +1381,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { // when assigning a material, search for its presence and, if not found, // place it in the first empty slot if (crossing.material != 0) { - for (int j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { + for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { if (materials[j] == crossing.material) { materialWeights[j] += 1.0f; totalWeight += 1.0f; @@ -1374,18 +1396,15 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } } } - glm::vec3 normal = safeNormalize(axisNormals[0] + axisNormals[1] + axisNormals[2]); center /= crossingCount; // use a sequence of Givens rotations to perform a QR decomposition // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf glm::mat4 r(0.0f); glm::vec4 bottom; - float smallestCosNormal = 1.0f; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); - smallestCosNormal = qMin(smallestCosNormal, glm::dot(crossing.normal, normal)); for (int j = 0; j < 4; j++) { float angle = glm::atan(-bottom[j], r[j][j]); @@ -1450,63 +1469,17 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + center) * scale, { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, - { (char)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) }, + { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; - // determine whether we must "crease" by generating directional normals - const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); - AxisIndex index(vertices.size(), vertices.size(), vertices.size()); - if (smallestCosNormal > CREASE_COS_NORMAL) { + NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; + vertices.append(point); + for (int i = 1; i < normalCount; i++) { + index.indices[i] = vertices.size(); + point.setNormal(normals[i]); vertices.append(point); - - } else { - axisNormals[0] = safeNormalize(axisNormals[0]); - axisNormals[1] = safeNormalize(axisNormals[1]); - axisNormals[2] = safeNormalize(axisNormals[2]); - glm::vec3 normalXY(safeNormalize(axisNormals[0] + axisNormals[1])); - glm::vec3 normalXZ(safeNormalize(axisNormals[0] + axisNormals[2])); - glm::vec3 normalYZ(safeNormalize(axisNormals[1] + axisNormals[2])); - if (glm::dot(axisNormals[0], normalXY) > CREASE_COS_NORMAL && - glm::dot(axisNormals[1], normalXY) > CREASE_COS_NORMAL) { - point.setNormal(normalXY); - vertices.append(point); - - point.setNormal(axisNormals[2]); - index.z = vertices.size(); - vertices.append(point); - - } else if (glm::dot(axisNormals[0], normalXZ) > CREASE_COS_NORMAL && - glm::dot(axisNormals[2], normalXZ) > CREASE_COS_NORMAL) { - point.setNormal(normalXZ); - vertices.append(point); - - point.setNormal(axisNormals[1]); - index.y = vertices.size(); - vertices.append(point); - - } else if (glm::dot(axisNormals[1], normalYZ) > CREASE_COS_NORMAL && - glm::dot(axisNormals[2], normalYZ) > CREASE_COS_NORMAL) { - point.setNormal(normalYZ); - vertices.append(point); - - point.setNormal(axisNormals[0]); - index.x = vertices.size(); - vertices.append(point); - - } else { - point.setNormal(axisNormals[0]); - vertices.append(point); - - point.setNormal(axisNormals[1]); - index.y = vertices.size(); - vertices.append(point); - - point.setNormal(axisNormals[2]); - index.z = vertices.size(); - vertices.append(point); - } } // the first x, y, and z are repeated for the boundary edge; past that, we consider generating @@ -1517,19 +1490,22 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - indices.append(index.x); - int index1 = lastLineIndices.at(x).x; - int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x; - int index3 = lastPlaneIndices.at(y * expanded + x).x; + + glm::vec3 normal(1.0f, 0.0f, 0.0f); + + const NormalIndex& index1 = lastLineIndices.at(x); + const NormalIndex& index2 = lastPlaneIndices.at((y - 1) * expanded + x); + const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); if (alpha0 == 0) { // quad faces negative x - indices.append(index3); - indices.append(index2); - indices.append(index1); + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive x - indices.append(index1); - indices.append(index2); - indices.append(index3); + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha2) { @@ -1537,19 +1513,22 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - indices.append(index.y); - int index1 = lastIndex.y; - int index2 = lastPlaneIndices.at(y * expanded + x - 1).y; - int index3 = lastPlaneIndices.at(y * expanded + x).y; + + glm::vec3 normal(0.0f, 1.0f, 0.0f); + + const NormalIndex& index1 = lastIndex; + const NormalIndex& index2 = lastPlaneIndices.at(y * expanded + x - 1); + const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); if (alpha0 == 0) { // quad faces negative y - indices.append(index1); - indices.append(index2); - indices.append(index3); + indices.append(index1.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } else { // quad faces positive y - indices.append(index3); - indices.append(index2); - indices.append(index1); + indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha4) { @@ -1557,19 +1536,22 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); - indices.append(index.z); - int index1 = lastIndex.z; - int index2 = lastLineIndices.at(x - 1).z; - int index3 = lastLineIndices.at(x).z; + + glm::vec3 normal(0.0f, 0.0f, 1.0f); + + const NormalIndex& index1 = lastIndex; + const NormalIndex& index2 = lastLineIndices.at(x - 1); + const NormalIndex& index3 = lastLineIndices.at(x); if (alpha0 == 0) { // quad faces negative z - indices.append(index3); - indices.append(index2); - indices.append(index1); + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive z - indices.append(index1); - indices.append(index2); - indices.append(index3); + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); } } lastIndex = index; From fa122b505ad825f7dc9272f019a177cacaa2d20d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Nov 2014 17:38:45 -0800 Subject: [PATCH 30/38] Further improvement to normals. --- interface/src/MetavoxelSystem.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9ab06f1006..e5453204ae 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1491,11 +1491,14 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - glm::vec3 normal(1.0f, 0.0f, 0.0f); - const NormalIndex& index1 = lastLineIndices.at(x); const NormalIndex& index2 = lastPlaneIndices.at((y - 1) * expanded + x); const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + if (alpha0 == 0) { // quad faces negative x indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); @@ -1514,11 +1517,14 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - glm::vec3 normal(0.0f, 1.0f, 0.0f); - const NormalIndex& index1 = lastIndex; const NormalIndex& index2 = lastPlaneIndices.at(y * expanded + x - 1); const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, + vertices.at(index1.indices[0]).vertex - first); + if (alpha0 == 0) { // quad faces negative y indices.append(index1.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); @@ -1537,11 +1543,14 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); - glm::vec3 normal(0.0f, 0.0f, 1.0f); - const NormalIndex& index1 = lastIndex; const NormalIndex& index2 = lastLineIndices.at(x - 1); const NormalIndex& index3 = lastLineIndices.at(x); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + if (alpha0 == 0) { // quad faces negative z indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); From d351a65999b44bd5c81a7b0689166ffb6446a836 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 12:09:33 -0800 Subject: [PATCH 31/38] Increase the smoothing cutoff. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e5453204ae..afa6d3a7d7 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1346,7 +1346,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { glm::vec3 center; glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; int normalCount = 0; - const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); + const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); const int MAX_MATERIALS_PER_VERTEX = 4; quint8 materials[] = { 0, 0, 0, 0 }; glm::vec4 materialWeights; From 53809803ddc39b095a632d387190875abc66a1b4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 12:56:56 -0800 Subject: [PATCH 32/38] These edits need to be sent reliably --- interface/src/ui/MetavoxelEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 43b542213d..7a24b4d71c 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -673,7 +673,7 @@ InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message); + Application::getInstance()->getMetavoxels()->applyEdit(message, true); } RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : @@ -857,7 +857,7 @@ void ImportHeightfieldTool::apply() { _heightOffset->value(), (i / HEIGHTFIELD_BLOCK_SIZE) * scale)); MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, heightfield)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message); + Application::getInstance()->getMetavoxels()->applyEdit(message, true); } } } From da85714dc8fea37da264757dc888dbba871206f1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 15:09:38 -0800 Subject: [PATCH 33/38] Batch heightfield sections for better performance. --- interface/src/MetavoxelSystem.cpp | 374 ++++++++++++++--------- interface/src/MetavoxelSystem.h | 52 +++- interface/src/renderer/ProgramObject.cpp | 16 + interface/src/renderer/ProgramObject.h | 4 + 4 files changed, 303 insertions(+), 143 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index afa6d3a7d7..0a32fd1c6e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -140,6 +140,18 @@ int RenderVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } +class HeightfieldPoint { +public: + glm::vec3 vertex; + glm::vec2 textureCoord; +}; + +const int SPLAT_COUNT = 4; +const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; + +static const int EIGHT_BIT_MAXIMUM = 255; +static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; + void MetavoxelSystem::render() { // update the frustum ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); @@ -150,6 +162,154 @@ void MetavoxelSystem::render() { RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor, true); + if (!_heightfieldBaseBatches.isEmpty()) { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + + foreach (const HeightfieldBaseBatch& batch, _heightfieldBaseBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniform( + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), batch.heightScale); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniform( + DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), batch.colorScale); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, batch.colorTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + glPopMatrix(); + } + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + + if (!_heightfieldSplatBatches.isEmpty()) { + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); + const DefaultMetavoxelRendererImplementation::SplatLocations& locations = + DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); + + foreach (const HeightfieldSplatBatch& batch, _heightfieldSplatBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.heightScale, batch.heightScale.x, batch.heightScale.y); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform( + locations.textureScale, batch.textureScale); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform(locations.splatTextureOffset, + batch.splatTextureOffset); + + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform( + locations.splatTextureScalesS, batch.splatTextureScalesS); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform( + locations.splatTextureScalesT, batch.splatTextureScalesT); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureValueMinima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( + locations.textureValueMaxima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, batch.materialTextureID); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, batch.splatTextureIDs[i]); + } + + glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + glPopMatrix(); + } + + DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthMask(true); + glDepthFunc(GL_LESS); + + _heightfieldSplatBatches.clear(); + } + + glDisable(GL_CULL_FACE); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + _heightfieldBaseBatches.clear(); + } + // give external parties a chance to join in emit rendering(); } @@ -670,12 +830,6 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { BufferData::~BufferData() { } -const int SPLAT_COUNT = 4; -const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; - -static const int EIGHT_BIT_MAXIMUM = 255; -static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; - void VoxelPoint::setNormal(const glm::vec3& normal) { this->normal[0] = (char)(normal.x * 127.0f); this->normal[1] = (char)(normal.y * 127.0f); @@ -1937,12 +2091,6 @@ void HeightfieldRenderer::init(Spanner* spanner) { connect(heightfield, &Heightfield::materialChanged, this, &HeightfieldRenderer::applyMaterial); } -class HeightfieldPoint { -public: - glm::vec3 vertex; - glm::vec2 textureCoord; -}; - void HeightfieldRenderer::render(bool cursor) { // create the buffer objects lazily Heightfield* heightfield = static_cast(_spanner); @@ -1984,6 +2132,7 @@ void HeightfieldRenderer::render(bool cursor) { bufferPair.first.create(); bufferPair.first.bind(); bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint)); + bufferPair.first.release(); QVector indices(indexCount); int* index = indices.data(); @@ -2005,150 +2154,91 @@ void HeightfieldRenderer::render(bool cursor) { bufferPair.second.create(); bufferPair.second.bind(); bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int)); - - } else { + bufferPair.second.release(); + } + + float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ(); + if (cursor) { bufferPair.first.bind(); bufferPair.second.bind(); - } - glPushMatrix(); - glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z); - glm::vec3 axis = glm::axis(heightfield->getRotation()); - glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z); - float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ(); - glScalef(xScale, xScale * heightfield->getAspectY(), zScale); - HeightfieldPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); - glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - - if (cursor) { - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + glPushMatrix(); + glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z); + glm::vec3 axis = glm::axis(heightfield->getRotation()); + glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z); + glScalef(xScale, xScale * heightfield->getAspectY(), zScale); - } else { - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / width, 1.0f / height, - (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)width / innerWidth, - (float)height / innerHeight); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); glBindTexture(GL_TEXTURE_2D, 0); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); + glPopMatrix(); - glDisable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - - if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.heightScale, 1.0f / width, 1.0f / height); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureScale, (float)width / innerWidth, (float)height / innerHeight); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(locations.splatTextureOffset, - glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, - glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); - - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - - const QVector& materials = heightfield->getMaterial()->getMaterials(); - for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { - QVector4D scalesS, scalesT; - - for (int j = 0; j < SPLAT_COUNT; j++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); - int index = i + j; - if (index < _networkTextures.size()) { - const NetworkTexturePointer& texture = _networkTextures.at(index); - if (texture) { - MaterialObject* material = static_cast(materials.at(index).data()); - scalesS[j] = xScale / material->getScaleS(); - scalesT[j] = zScale / material->getScaleT(); - glBindTexture(GL_TEXTURE_2D, texture->getID()); - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesS, scalesS); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesT, scalesT); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMinima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMaxima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - } - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); - - glDisable(GL_POLYGON_OFFSET_FILL); - glDepthMask(true); - glDepthFunc(GL_LESS); - } - glActiveTexture(GL_TEXTURE0); - - glDisable(GL_CULL_FACE); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); + bufferPair.first.release(); + bufferPair.second.release(); + return; } - glBindTexture(GL_TEXTURE_2D, 0); + HeightfieldBaseBatch baseBatch; + baseBatch.vertexBuffer = &bufferPair.first; + baseBatch.indexBuffer = &bufferPair.second; + baseBatch.translation = heightfield->getTranslation(); + baseBatch.rotation = heightfield->getRotation(); + baseBatch.scale = glm::vec3(xScale, xScale * heightfield->getAspectY(), zScale); + baseBatch.vertexCount = vertexCount; + baseBatch.indexCount = indexCount; + baseBatch.heightTextureID = _heightTextureID; + baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); + baseBatch.colorTextureID = _colorTextureID; + baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); + Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); - glPopMatrix(); - - bufferPair.first.release(); - bufferPair.second.release(); + if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { + HeightfieldSplatBatch splatBatch; + splatBatch.vertexBuffer = &bufferPair.first; + splatBatch.indexBuffer = &bufferPair.second; + splatBatch.translation = heightfield->getTranslation(); + splatBatch.rotation = heightfield->getRotation(); + splatBatch.scale = glm::vec3(xScale, xScale * heightfield->getAspectY(), zScale); + splatBatch.vertexCount = vertexCount; + splatBatch.indexCount = indexCount; + splatBatch.heightTextureID = _heightTextureID; + splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f); + splatBatch.materialTextureID = _materialTextureID; + splatBatch.textureScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); + splatBatch.splatTextureOffset = glm::vec2( + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); + + const QVector& materials = heightfield->getMaterial()->getMaterials(); + for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { + for (int j = 0; j < SPLAT_COUNT; j++) { + int index = i + j; + if (index < _networkTextures.size()) { + const NetworkTexturePointer& texture = _networkTextures.at(index); + if (texture) { + MaterialObject* material = static_cast(materials.at(index).data()); + splatBatch.splatTextureScalesS[j] = xScale / material->getScaleS(); + splatBatch.splatTextureScalesT[j] = zScale / material->getScaleT(); + splatBatch.splatTextureIDs[j] = texture->getID(); + + } else { + splatBatch.splatTextureIDs[j] = 0; + } + } else { + splatBatch.splatTextureIDs[j] = 0; + } + } + splatBatch.materialIndex = i; + Application::getInstance()->getMetavoxels()->addHeightfieldSplatBatch(splatBatch); + } + } } void HeightfieldRenderer::applyHeight(const HeightfieldHeightPointer& height) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 90560e63aa..ef1af3fa25 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -23,6 +23,8 @@ #include "renderer/ProgramObject.h" +class HeightfieldBaseBatch; +class HeightfieldSplatBatch; class Model; /// Renders a metavoxel tree. @@ -77,7 +79,10 @@ public: Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color); Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); - + + void addHeightfieldBaseBatch(const HeightfieldBaseBatch& batch) { _heightfieldBaseBatches.append(batch); } + void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } + signals: void rendering(); @@ -105,6 +110,51 @@ private: NetworkSimulation _networkSimulation; QReadWriteLock _networkSimulationLock; + + QVector _heightfieldBaseBatches; + QVector _heightfieldSplatBatches; +}; + +/// Base class for heightfield batches. +class HeightfieldBatch { +public: + QOpenGLBuffer* vertexBuffer; + QOpenGLBuffer* indexBuffer; + glm::vec3 translation; + glm::quat rotation; + glm::vec3 scale; + int vertexCount; + int indexCount; + GLuint heightTextureID; + glm::vec4 heightScale; +}; + +/// A batch containing a heightfield base. +class HeightfieldBaseBatch : public HeightfieldBatch { +public: + QOpenGLBuffer* vertexBuffer; + QOpenGLBuffer* indexBuffer; + glm::vec3 translation; + glm::quat rotation; + glm::vec3 scale; + int vertexCount; + int indexCount; + GLuint heightTextureID; + GLuint colorTextureID; + glm::vec4 heightScale; + glm::vec2 colorScale; +}; + +/// A batch containing a heightfield splat. +class HeightfieldSplatBatch : public HeightfieldBatch { +public: + GLuint materialTextureID; + glm::vec2 textureScale; + glm::vec2 splatTextureOffset; + int splatTextureIDs[4]; + glm::vec4 splatTextureScalesS; + glm::vec4 splatTextureScalesT; + int materialIndex; }; /// Generic abstract base class for objects that handle a signal. diff --git a/interface/src/renderer/ProgramObject.cpp b/interface/src/renderer/ProgramObject.cpp index 16b3461ad0..ff2b1cc11e 100644 --- a/interface/src/renderer/ProgramObject.cpp +++ b/interface/src/renderer/ProgramObject.cpp @@ -15,6 +15,14 @@ ProgramObject::ProgramObject(QObject* parent) : QGLShaderProgram(parent) { } +void ProgramObject::setUniform(int location, const glm::vec2& value) { + setUniformValue(location, value.x, value.y); +} + +void ProgramObject::setUniform(const char* name, const glm::vec2& value) { + setUniformValue(name, value.x, value.y); +} + void ProgramObject::setUniform(int location, const glm::vec3& value) { setUniformValue(location, value.x, value.y, value.z); } @@ -23,6 +31,14 @@ void ProgramObject::setUniform(const char* name, const glm::vec3& value) { setUniformValue(name, value.x, value.y, value.z); } +void ProgramObject::setUniform(int location, const glm::vec4& value) { + setUniformValue(location, value.x, value.y, value.z, value.w); +} + +void ProgramObject::setUniform(const char* name, const glm::vec4& value) { + setUniformValue(name, value.x, value.y, value.z, value.w); +} + void ProgramObject::setUniformArray(const char* name, const glm::vec3* values, int count) { GLfloat* floatVal = new GLfloat[count*3]; int index = 0; diff --git a/interface/src/renderer/ProgramObject.h b/interface/src/renderer/ProgramObject.h index 8e66ce9bc9..5014c6d32b 100644 --- a/interface/src/renderer/ProgramObject.h +++ b/interface/src/renderer/ProgramObject.h @@ -21,8 +21,12 @@ public: ProgramObject(QObject* parent = 0); + void setUniform(int location, const glm::vec2& value); + void setUniform(const char* name, const glm::vec2& value); void setUniform(int location, const glm::vec3& value); void setUniform(const char* name, const glm::vec3& value); + void setUniform(int location, const glm::vec4& value); + void setUniform(const char* name, const glm::vec4& value); void setUniformArray(const char* name, const glm::vec3* values, int count); }; From 1e294e043b8ddba8e441f7c447a6b3e470718e19 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 16:37:37 -0800 Subject: [PATCH 34/38] Batch voxel drawing, too. --- interface/src/MetavoxelSystem.cpp | 468 ++++++++++++++++-------------- interface/src/MetavoxelSystem.h | 121 ++++---- 2 files changed, 309 insertions(+), 280 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 0a32fd1c6e..7129ac4e88 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -56,12 +56,50 @@ MetavoxelSystem::~MetavoxelSystem() { void MetavoxelSystem::init() { MetavoxelClientManager::init(); - DefaultMetavoxelRendererImplementation::init(); _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("voxelBuffer")); _voxelBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier()); + + _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_base.vert"); + _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_base.frag"); + _baseHeightfieldProgram.link(); + + _baseHeightfieldProgram.bind(); + _baseHeightfieldProgram.setUniformValue("heightMap", 0); + _baseHeightfieldProgram.setUniformValue("diffuseMap", 1); + _baseHeightScaleLocation = _baseHeightfieldProgram.uniformLocation("heightScale"); + _baseColorScaleLocation = _baseHeightfieldProgram.uniformLocation("colorScale"); + _baseHeightfieldProgram.release(); + + loadSplatProgram("heightfield", _splatHeightfieldProgram, _splatHeightfieldLocations); + + _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_cursor.vert"); + _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_cursor.frag"); + _heightfieldCursorProgram.link(); + + _heightfieldCursorProgram.bind(); + _heightfieldCursorProgram.setUniformValue("heightMap", 0); + _heightfieldCursorProgram.release(); + + _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_voxel_base.vert"); + _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_voxel_base.frag"); + _baseVoxelProgram.link(); + + loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); + + _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_voxel_cursor.vert"); + _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_cursor.frag"); + _voxelCursorProgram.link(); } MetavoxelLOD MetavoxelSystem::getLOD() { @@ -175,7 +213,7 @@ void MetavoxelSystem::render() { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + _baseHeightfieldProgram.bind(); foreach (const HeightfieldBaseBatch& batch, _heightfieldBaseBatches) { glPushMatrix(); @@ -193,10 +231,8 @@ void MetavoxelSystem::render() { glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniform( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), batch.heightScale); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniform( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), batch.colorScale); + _baseHeightfieldProgram.setUniform(_baseHeightScaleLocation, batch.heightScale); + _baseHeightfieldProgram.setUniform(_baseColorScaleLocation, batch.colorScale); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, batch.colorTextureID); @@ -208,12 +244,15 @@ void MetavoxelSystem::render() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + glPopMatrix(); } Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); + _baseHeightfieldProgram.release(); glDisable(GL_ALPHA_TEST); glEnable(GL_BLEND); @@ -224,10 +263,8 @@ void MetavoxelSystem::render() { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -1.0f); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); - + _splatHeightfieldProgram.bind(); + foreach (const HeightfieldSplatBatch& batch, _heightfieldSplatBatches) { glPushMatrix(); glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); @@ -244,26 +281,22 @@ void MetavoxelSystem::render() { glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.heightScale, batch.heightScale.x, batch.heightScale.y); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform( - locations.textureScale, batch.textureScale); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform(locations.splatTextureOffset, - batch.splatTextureOffset); + _splatHeightfieldProgram.setUniformValue(_splatHeightfieldLocations.heightScale, + batch.heightScale.x, batch.heightScale.y); + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.textureScale, batch.textureScale); + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureOffset, batch.splatTextureOffset); const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform( - locations.splatTextureScalesS, batch.splatTextureScalesS); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniform( - locations.splatTextureScalesT, batch.splatTextureScalesT); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMinima, + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureScalesS, batch.splatTextureScalesS); + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureScalesT, batch.splatTextureScalesT); + _splatHeightfieldProgram.setUniformValue( + _splatHeightfieldLocations.textureValueMinima, (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMaxima, + _splatHeightfieldProgram.setUniformValue( + _splatHeightfieldLocations.textureValueMaxima, (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, @@ -290,10 +323,13 @@ void MetavoxelSystem::render() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + glPopMatrix(); } - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release(); + _splatHeightfieldProgram.release(); glDisable(GL_POLYGON_OFFSET_FILL); glDepthMask(true); @@ -310,6 +346,121 @@ void MetavoxelSystem::render() { _heightfieldBaseBatches.clear(); } + if (!_voxelBaseBatches.isEmpty()) { + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glEnableClientState(GL_VERTEX_ARRAY); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + _baseVoxelProgram.bind(); + + foreach (const VoxelBaseBatch& batch, _voxelBaseBatches) { + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + } + + _baseVoxelProgram.release(); + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); + + if (!_voxelSplatBatches.isEmpty()) { + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + _splatVoxelProgram.bind(); + + _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materials); + _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materialWeights); + + foreach (const VoxelSplatBatch& batch, _voxelSplatBatches) { + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materials, + GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); + _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materialWeights, + GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); + + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesS, batch.splatTextureScalesS); + _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesT, batch.splatTextureScalesT); + _splatVoxelProgram.setUniformValue( + _splatVoxelLocations.textureValueMinima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + _splatVoxelProgram.setUniformValue( + _splatVoxelLocations.textureValueMaxima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, batch.splatTextureIDs[i]); + } + + glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + } + + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthMask(true); + glDepthFunc(GL_LESS); + + _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materials); + _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materialWeights); + + _voxelSplatBatches.clear(); + } + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisable(GL_CULL_FACE); + + _voxelBaseBatches.clear(); + } + // give external parties a chance to join in emit rendering(); } @@ -440,7 +591,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); + _heightfieldCursorProgram.bind(); glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; @@ -456,7 +607,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r SpannerCursorRenderVisitor visitor(Box(position - extents, position + extents)); guide(visitor); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); + _heightfieldCursorProgram.release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); @@ -504,7 +655,7 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glEnableClientState(GL_VERTEX_ARRAY); - DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().bind(); + _voxelCursorProgram.bind(); glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; @@ -521,16 +672,16 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) BufferCursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds); guideToAugmented(voxelVisitor); - DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); + _voxelCursorProgram.release(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); + _heightfieldCursorProgram.bind(); SpannerCursorRenderVisitor spannerVisitor(bounds); guide(spannerVisitor); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); + _heightfieldCursorProgram.release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -612,6 +763,29 @@ void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { } } +void MetavoxelSystem::loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations) { + program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_" + type + "_splat.vert"); + program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_" + type + "_splat.frag"); + program.link(); + + program.bind(); + program.setUniformValue("heightMap", 0); + program.setUniformValue("textureMap", 1); + program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); + locations.heightScale = program.uniformLocation("heightScale"); + locations.textureScale = program.uniformLocation("textureScale"); + locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); + locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); + locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); + locations.textureValueMinima = program.uniformLocation("textureValueMinima"); + locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); + locations.materials = program.attributeLocation("materials"); + locations.materialWeights = program.attributeLocation("materialWeights"); + program.release(); +} + Throttle::Throttle() : _limit(INT_MAX), _total(0) { @@ -946,10 +1120,12 @@ void VoxelBuffer::render(bool cursor) { _vertexBuffer.create(); _vertexBuffer.bind(); _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); + _vertexBuffer.release(); _indexBuffer.create(); _indexBuffer.bind(); _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); + _indexBuffer.release(); if (!_materials.isEmpty()) { _networkTextures.resize(_materials.size()); @@ -961,101 +1137,62 @@ void VoxelBuffer::render(bool cursor) { } } } - } else { - _vertexBuffer.bind(); - _indexBuffer.bind(); } - VoxelPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + if (cursor) { + _vertexBuffer.bind(); + _indexBuffer.bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + + _vertexBuffer.release(); + _indexBuffer.release(); + return; + } - glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + VoxelBaseBatch baseBatch; + baseBatch.vertexBuffer = &_vertexBuffer; + baseBatch.indexBuffer = &_indexBuffer; + baseBatch.vertexCount = _vertexCount; + baseBatch.indexCount = _indexCount; + Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); + + if (!_materials.isEmpty()) { + VoxelSplatBatch splatBatch; + splatBatch.vertexBuffer = &_vertexBuffer; + splatBatch.indexBuffer = &_indexBuffer; + splatBatch.vertexCount = _vertexCount; + splatBatch.indexCount = _indexCount; - if (!(_materials.isEmpty() || cursor)) { - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatVoxelLocations(); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setAttributeBuffer(locations.materials, - GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().enableAttributeArray(locations.materials); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setAttributeBuffer(locations.materialWeights, - GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().enableAttributeArray(locations.materialWeights); - for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { - QVector4D scalesS, scalesT; - for (int j = 0; j < SPLAT_COUNT; j++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); int index = i + j; if (index < _networkTextures.size()) { const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { MaterialObject* material = static_cast(_materials.at(index).data()); - scalesS[j] = 1.0f / material->getScaleS(); - scalesT[j] = 1.0f / material->getScaleT(); - glBindTexture(GL_TEXTURE_2D, texture->getID()); + splatBatch.splatTextureScalesS[j] = 1.0f / material->getScaleS(); + splatBatch.splatTextureScalesT[j] = 1.0f / material->getScaleT(); + splatBatch.splatTextureIDs[j] = texture->getID(); + } else { - glBindTexture(GL_TEXTURE_2D, 0); + splatBatch.splatTextureIDs[j] = 0; } } else { - glBindTexture(GL_TEXTURE_2D, 0); + splatBatch.splatTextureIDs[j] = 0; } } - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.splatTextureScalesS, scalesS); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.splatTextureScalesT, scalesT); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.textureValueMinima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.textureValueMaxima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + splatBatch.materialIndex = i; + Application::getInstance()->getMetavoxels()->addVoxelSplatBatch(splatBatch); } - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE0); - - glDisable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_ALPHA_TEST); - glDisable(GL_BLEND); - glDepthMask(true); - glDepthFunc(GL_LESS); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().disableAttributeArray(locations.materials); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().disableAttributeArray(locations.materialWeights); - - DefaultMetavoxelRendererImplementation::getBaseVoxelProgram().bind(); } - _vertexBuffer.release(); - _indexBuffer.release(); - - if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData) && !cursor) { + if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); @@ -1066,9 +1203,6 @@ void VoxelBuffer::render(bool cursor) { _hermiteBuffer.bind(); } - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, 0); Application::getInstance()->getDeferredLightingEffect()->getSimpleProgram().bind(); @@ -1080,10 +1214,7 @@ void VoxelBuffer::render(bool cursor) { glDrawArrays(GL_LINES, 0, _hermiteCount); - DefaultMetavoxelRendererImplementation::getBaseVoxelProgram().bind(); - - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); + Application::getInstance()->getDeferredLightingEffect()->getSimpleProgram().release(); _hermiteBuffer.release(); } @@ -1107,49 +1238,6 @@ AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) c return AttributeValue(parentValue.getAttribute()); } -void DefaultMetavoxelRendererImplementation::init() { - if (!_baseHeightfieldProgram.isLinked()) { - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_base.vert"); - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_base.frag"); - _baseHeightfieldProgram.link(); - - _baseHeightfieldProgram.bind(); - _baseHeightfieldProgram.setUniformValue("heightMap", 0); - _baseHeightfieldProgram.setUniformValue("diffuseMap", 1); - _baseHeightScaleLocation = _baseHeightfieldProgram.uniformLocation("heightScale"); - _baseColorScaleLocation = _baseHeightfieldProgram.uniformLocation("colorScale"); - _baseHeightfieldProgram.release(); - - loadSplatProgram("heightfield", _splatHeightfieldProgram, _splatHeightfieldLocations); - - _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_cursor.vert"); - _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_cursor.frag"); - _heightfieldCursorProgram.link(); - - _heightfieldCursorProgram.bind(); - _heightfieldCursorProgram.setUniformValue("heightMap", 0); - _heightfieldCursorProgram.release(); - - _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_voxel_base.vert"); - _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_voxel_base.frag"); - _baseVoxelProgram.link(); - - loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); - - _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_voxel_cursor.vert"); - _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_cursor.frag"); - _voxelCursorProgram.link(); - } -} - DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } @@ -1872,76 +1960,12 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox SpannerRenderVisitor spannerRenderVisitor(lod); data.guide(spannerRenderVisitor); } - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - glEnableClientState(GL_VERTEX_ARRAY); - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - _baseVoxelProgram.bind(); - BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); - - _baseVoxelProgram.release(); - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); } - - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - - glDisableClientState(GL_VERTEX_ARRAY); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); } -void DefaultMetavoxelRendererImplementation::loadSplatProgram(const char* type, - ProgramObject& program, SplatLocations& locations) { - program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_" + type + "_splat.vert"); - program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_" + type + "_splat.frag"); - program.link(); - - program.bind(); - program.setUniformValue("heightMap", 0); - program.setUniformValue("textureMap", 1); - program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); - locations.heightScale = program.uniformLocation("heightScale"); - locations.textureScale = program.uniformLocation("textureScale"); - locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); - locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); - locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); - locations.textureValueMinima = program.uniformLocation("textureValueMinima"); - locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); - locations.materials = program.attributeLocation("materials"); - locations.materialWeights = program.attributeLocation("materialWeights"); - program.release(); -} - -ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation; -int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram; -DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatHeightfieldLocations; -ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; -ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram; -ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram; -DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations; -ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram; - SphereRenderer::SphereRenderer() { } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index ef1af3fa25..90a04ae417 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -26,6 +26,8 @@ class HeightfieldBaseBatch; class HeightfieldSplatBatch; class Model; +class VoxelBaseBatch; +class VoxelSplatBatch; /// Renders a metavoxel tree. class MetavoxelSystem : public MetavoxelClientManager { @@ -83,6 +85,9 @@ public: void addHeightfieldBaseBatch(const HeightfieldBaseBatch& batch) { _heightfieldBaseBatches.append(batch); } void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } + void addVoxelBaseBatch(const VoxelBaseBatch& batch) { _voxelBaseBatches.append(batch); } + void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } + signals: void rendering(); @@ -113,6 +118,46 @@ private: QVector _heightfieldBaseBatches; QVector _heightfieldSplatBatches; + QVector _voxelBaseBatches; + QVector _voxelSplatBatches; + + ProgramObject _baseHeightfieldProgram; + int _baseHeightScaleLocation; + int _baseColorScaleLocation; + + class SplatLocations { + public: + int heightScale; + int textureScale; + int splatTextureOffset; + int splatTextureScalesS; + int splatTextureScalesT; + int textureValueMinima; + int textureValueMaxima; + int materials; + int materialWeights; + }; + + ProgramObject _splatHeightfieldProgram; + SplatLocations _splatHeightfieldLocations; + + int _splatHeightScaleLocation; + int _splatTextureScaleLocation; + int _splatTextureOffsetLocation; + int _splatTextureScalesSLocation; + int _splatTextureScalesTLocation; + int _splatTextureValueMinimaLocation; + int _splatTextureValueMaximaLocation; + + ProgramObject _heightfieldCursorProgram; + + ProgramObject _baseVoxelProgram; + ProgramObject _splatVoxelProgram; + SplatLocations _splatVoxelLocations; + + ProgramObject _voxelCursorProgram; + + static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); }; /// Base class for heightfield batches. @@ -157,6 +202,24 @@ public: int materialIndex; }; +/// Base class for voxel batches. +class VoxelBaseBatch { +public: + QOpenGLBuffer* vertexBuffer; + QOpenGLBuffer* indexBuffer; + int vertexCount; + int indexCount; +}; + +/// A batch containing a voxel splat. +class VoxelSplatBatch : public VoxelBaseBatch { +public: + int splatTextureIDs[4]; + glm::vec4 splatTextureScalesS; + glm::vec4 splatTextureScalesT; + int materialIndex; +}; + /// Generic abstract base class for objects that handle a signal. class SignalHandler : public QObject { Q_OBJECT @@ -296,69 +359,11 @@ class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplement public: - static void init(); - - static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; } - static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; } - static int getBaseColorScaleLocation() { return _baseColorScaleLocation; } - - class SplatLocations { - public: - int heightScale; - int textureScale; - int splatTextureOffset; - int splatTextureScalesS; - int splatTextureScalesT; - int textureValueMinima; - int textureValueMaxima; - int materials; - int materialWeights; - }; - - static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; } - static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; } - - static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; } - - static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; } - - static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; } - static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; } - - static ProgramObject& getVoxelCursorProgram() { return _voxelCursorProgram; } - 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 void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); - - static ProgramObject _baseHeightfieldProgram; - static int _baseHeightScaleLocation; - static int _baseColorScaleLocation; - - static ProgramObject _splatHeightfieldProgram; - static SplatLocations _splatHeightfieldLocations; - - static int _splatHeightScaleLocation; - static int _splatTextureScaleLocation; - static int _splatTextureOffsetLocation; - static int _splatTextureScalesSLocation; - static int _splatTextureScalesTLocation; - static int _splatTextureValueMinimaLocation; - static int _splatTextureValueMaximaLocation; - - static ProgramObject _heightfieldCursorProgram; - - static ProgramObject _baseVoxelProgram; - static ProgramObject _splatVoxelProgram; - static SplatLocations _splatVoxelLocations; - - static ProgramObject _voxelCursorProgram; }; /// Renders spheres. From f2f7335f58c4fc12257832e37218ddb6b0f2ca8a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 17:16:52 -0800 Subject: [PATCH 35/38] Increase the block size, reduce the overhead. --- interface/src/ui/MetavoxelEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 7a24b4d71c..b6b43c4baf 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -792,7 +792,7 @@ void ImportHeightfieldTool::renderPreview() { static_cast(_spanner.data())->getRenderer()->render(); } -const int HEIGHTFIELD_BLOCK_SIZE = 32; +const int HEIGHTFIELD_BLOCK_SIZE = 256; void ImportHeightfieldTool::apply() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); From 03e302c508f9da3b6e971324a82a18bf46d30d50 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 17:34:37 -0800 Subject: [PATCH 36/38] Removed a debug statemen. --- libraries/metavoxels/src/MetavoxelData.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 535a336da7..c340a7dd4a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -744,7 +744,6 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l return false; } if (_roots.size() != other._roots.size()) { - qDebug() << _roots << other._roots; return false; } glm::vec3 minimum = getMinimum(); From 87cc565c1ac7105c12880be1d21f49b3cea31dc7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Nov 2014 18:16:57 -0800 Subject: [PATCH 37/38] I believe this should be a better hash function for the voxel coordinates. --- interface/src/MetavoxelSystem.cpp | 6 +++--- interface/src/MetavoxelSystem.h | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7129ac4e88..b0a5d3324c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1011,7 +1011,7 @@ void VoxelPoint::setNormal(const glm::vec3& normal) { } VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QMultiHash& quadIndices, int size, const QVector& materials) : + const QMultiHash& quadIndices, int size, const QVector& materials) : _vertices(vertices), _indices(indices), _hermite(hermite), @@ -1056,7 +1056,7 @@ bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::ve int max = _size - 2; int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); forever { - for (QMultiHash::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); + for (QMultiHash::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); it != _quadIndices.constEnd(); it++) { const int* indices = _indices.constData() + *it; if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, @@ -1311,7 +1311,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector vertices; QVector indices; QVector hermiteSegments; - QMultiHash quadIndices; + QMultiHash quadIndices; // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 90a04ae417..dfbabbb2bf 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -308,12 +308,27 @@ public: void setNormal(const glm::vec3& normal); }; +/// A container for a coordinate within a voxel block. +class VoxelCoord { +public: + QRgb encoded; + + VoxelCoord(QRgb encoded) : encoded(encoded) { } + + bool operator==(const VoxelCoord& other) const { return encoded == other.encoded; } +}; + +inline uint qHash(const VoxelCoord& coord, uint seed) { + // 31 is just an arbitrary prime number + return qHash(qRed(coord.encoded) + 31 * (qGreen(coord.encoded) + 31 * qBlue(coord.encoded)), seed); +} + /// Contains the information necessary to render a voxel block. class VoxelBuffer : public BufferData { public: VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QMultiHash& quadIndices, int size, const QVector& materials = + const QMultiHash& quadIndices, int size, const QVector& materials = QVector()); /// Finds the first intersection between the described ray and the voxel data. @@ -328,7 +343,7 @@ private: QVector _vertices; QVector _indices; QVector _hermite; - QMultiHash _quadIndices; + QMultiHash _quadIndices; int _size; int _vertexCount; int _indexCount; From 296718e2aae5b20b54990bb81a1d9c54f5bfb612 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Nov 2014 12:46:24 -0800 Subject: [PATCH 38/38] Removed redundant members, clarified class names, used better hash factors. --- interface/src/MetavoxelSystem.cpp | 8 ++++---- interface/src/MetavoxelSystem.h | 33 +++++++++++-------------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b0a5d3324c..b98fea8eca 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -215,7 +215,7 @@ void MetavoxelSystem::render() { _baseHeightfieldProgram.bind(); - foreach (const HeightfieldBaseBatch& batch, _heightfieldBaseBatches) { + foreach (const HeightfieldBaseLayerBatch& batch, _heightfieldBaseBatches) { glPushMatrix(); glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); glm::vec3 axis = glm::axis(batch.rotation); @@ -362,7 +362,7 @@ void MetavoxelSystem::render() { _baseVoxelProgram.bind(); - foreach (const VoxelBaseBatch& batch, _voxelBaseBatches) { + foreach (const VoxelBatch& batch, _voxelBaseBatches) { batch.vertexBuffer->bind(); batch.indexBuffer->bind(); @@ -1155,7 +1155,7 @@ void VoxelBuffer::render(bool cursor) { return; } - VoxelBaseBatch baseBatch; + VoxelBatch baseBatch; baseBatch.vertexBuffer = &_vertexBuffer; baseBatch.indexBuffer = &_indexBuffer; baseBatch.vertexCount = _vertexCount; @@ -2209,7 +2209,7 @@ void HeightfieldRenderer::render(bool cursor) { return; } - HeightfieldBaseBatch baseBatch; + HeightfieldBaseLayerBatch baseBatch; baseBatch.vertexBuffer = &bufferPair.first; baseBatch.indexBuffer = &bufferPair.second; baseBatch.translation = heightfield->getTranslation(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index dfbabbb2bf..c1cdfd3624 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -23,10 +23,10 @@ #include "renderer/ProgramObject.h" -class HeightfieldBaseBatch; +class HeightfieldBaseLayerBatch; class HeightfieldSplatBatch; class Model; -class VoxelBaseBatch; +class VoxelBatch; class VoxelSplatBatch; /// Renders a metavoxel tree. @@ -82,10 +82,10 @@ public: Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); - void addHeightfieldBaseBatch(const HeightfieldBaseBatch& batch) { _heightfieldBaseBatches.append(batch); } + void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); } void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } - void addVoxelBaseBatch(const VoxelBaseBatch& batch) { _voxelBaseBatches.append(batch); } + void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); } void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } signals: @@ -116,9 +116,9 @@ private: NetworkSimulation _networkSimulation; QReadWriteLock _networkSimulationLock; - QVector _heightfieldBaseBatches; + QVector _heightfieldBaseBatches; QVector _heightfieldSplatBatches; - QVector _voxelBaseBatches; + QVector _voxelBaseBatches; QVector _voxelSplatBatches; ProgramObject _baseHeightfieldProgram; @@ -174,19 +174,10 @@ public: glm::vec4 heightScale; }; -/// A batch containing a heightfield base. -class HeightfieldBaseBatch : public HeightfieldBatch { +/// A batch containing a heightfield base layer. +class HeightfieldBaseLayerBatch : public HeightfieldBatch { public: - QOpenGLBuffer* vertexBuffer; - QOpenGLBuffer* indexBuffer; - glm::vec3 translation; - glm::quat rotation; - glm::vec3 scale; - int vertexCount; - int indexCount; - GLuint heightTextureID; GLuint colorTextureID; - glm::vec4 heightScale; glm::vec2 colorScale; }; @@ -203,7 +194,7 @@ public: }; /// Base class for voxel batches. -class VoxelBaseBatch { +class VoxelBatch { public: QOpenGLBuffer* vertexBuffer; QOpenGLBuffer* indexBuffer; @@ -212,7 +203,7 @@ public: }; /// A batch containing a voxel splat. -class VoxelSplatBatch : public VoxelBaseBatch { +class VoxelSplatBatch : public VoxelBatch { public: int splatTextureIDs[4]; glm::vec4 splatTextureScalesS; @@ -319,8 +310,8 @@ public: }; inline uint qHash(const VoxelCoord& coord, uint seed) { - // 31 is just an arbitrary prime number - return qHash(qRed(coord.encoded) + 31 * (qGreen(coord.encoded) + 31 * qBlue(coord.encoded)), seed); + // multiply by prime numbers greater than the possible size + return qHash(qRed(coord.encoded) + 257 * (qGreen(coord.encoded) + 263 * qBlue(coord.encoded)), seed); } /// Contains the information necessary to render a voxel block.