From 60da0f1567f1df85bae60591a03e0a53bcf3c32f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Oct 2014 17:18:52 -0700 Subject: [PATCH] Allow drawing cursor on dual contour surface, wired up material paint tool. --- .../shaders/metavoxel_voxel_cursor.frag | 32 ++++++ .../shaders/metavoxel_voxel_cursor.vert | 25 +++++ interface/src/MetavoxelSystem.cpp | 61 ++++++++-- interface/src/MetavoxelSystem.h | 6 + interface/src/ui/MetavoxelEditor.cpp | 76 +++++++++++-- interface/src/ui/MetavoxelEditor.h | 21 ++++ .../metavoxels/src/MetavoxelMessages.cpp | 105 ++++++++++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 19 ++++ 8 files changed, 326 insertions(+), 19 deletions(-) create mode 100644 interface/resources/shaders/metavoxel_voxel_cursor.frag create mode 100644 interface/resources/shaders/metavoxel_voxel_cursor.vert diff --git a/interface/resources/shaders/metavoxel_voxel_cursor.frag b/interface/resources/shaders/metavoxel_voxel_cursor.frag new file mode 100644 index 0000000000..3a803ea309 --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_cursor.frag @@ -0,0 +1,32 @@ +#version 120 + +// +// metavoxel_voxel_base.frag +// fragment shader +// +// Created by Andrzej Kapolka on 10/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 +// + +// the inner radius of the outline, squared +const float SQUARED_OUTLINE_INNER_RADIUS = 0.81; + +// the outer radius of the outline, squared +const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0; + +// the inner radius of the inset, squared +const float SQUARED_INSET_INNER_RADIUS = 0.855625; + +// the outer radius of the inset, squared +const float SQUARED_INSET_OUTER_RADIUS = 0.950625; + +void main(void) { + // use the distance to compute the ring color, then multiply it by the varying color + float squaredDistance = dot(gl_TexCoord[0].str, gl_TexCoord[0].str); + float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS); + float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS); + gl_FragColor = gl_Color * vec4(white, white, white, alpha); +} diff --git a/interface/resources/shaders/metavoxel_voxel_cursor.vert b/interface/resources/shaders/metavoxel_voxel_cursor.vert new file mode 100644 index 0000000000..586eef275a --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_cursor.vert @@ -0,0 +1,25 @@ +#version 120 + +// +// metavoxel_voxel_cursor.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/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 +// + +void main(void) { + // compute the view space coordinates + vec4 viewPosition = gl_ModelViewMatrix * gl_Vertex; + gl_Position = gl_ProjectionMatrix * viewPosition; + + // generate the texture coordinates from the view position + gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]), + dot(viewPosition, gl_EyePlaneR[4]), 1.0); + + // copy the color for interpolation + gl_FrontColor = gl_Color; +} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 19a9a7ce89..24a1026ef4 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -454,10 +454,10 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { return visitor.height; } -class HeightfieldCursorRenderVisitor : public MetavoxelVisitor { +class CursorRenderVisitor : public MetavoxelVisitor { public: - HeightfieldCursorRenderVisitor(const Box& bounds); + CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); virtual int visit(MetavoxelInfo& info); @@ -466,13 +466,12 @@ private: Box _bounds; }; -HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) : - MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()), +CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : + MetavoxelVisitor(QVector() << attribute), _bounds(bounds) { } -int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) { +int CursorRenderVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; } @@ -508,7 +507,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents)); + CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), + Box(position - extents, position + extents)); guideToAugmented(visitor); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); @@ -521,6 +521,42 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glDepthFunc(GL_LESS); } +void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) { + glDepthFunc(GL_LEQUAL); + glEnable(GL_CULL_FACE); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnableClientState(GL_VERTEX_ARRAY); + + DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().bind(); + + glActiveTexture(GL_TEXTURE4); + float scale = 1.0f / radius; + glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); + glm::vec4 tCoefficients(0.0f, scale, 0.0f, -scale * position.y); + glm::vec4 rCoefficients(0.0f, 0.0f, scale, -scale * position.z); + glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients); + glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients); + glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients); + glActiveTexture(GL_TEXTURE0); + + glm::vec3 extents(radius, radius, radius); + CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), + Box(position - extents, position + extents)); + guideToAugmented(visitor); + + DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); + + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glDepthFunc(GL_LESS); +} + void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { glDeleteTextures(1, (GLuint*)&heightID); glDeleteTextures(1, (GLuint*)&colorID); @@ -1170,7 +1206,7 @@ void VoxelBuffer::render(bool cursor) { glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); - if (!_materials.isEmpty()) { + if (!(_materials.isEmpty() || cursor)) { Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); glDepthFunc(GL_LEQUAL); @@ -1252,7 +1288,7 @@ void VoxelBuffer::render(bool cursor) { _vertexBuffer.release(); _indexBuffer.release(); - if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { + if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData) && !cursor) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); @@ -1348,6 +1384,12 @@ void DefaultMetavoxelRendererImplementation::init() { _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_voxel_cursor.frag"); + _voxelCursorProgram.link(); } } @@ -2577,6 +2619,7 @@ ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram; 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 }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index daa1bd6ca2..f479ba5e50 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -46,6 +46,8 @@ public: void renderHeightfieldCursor(const glm::vec3& position, float radius); + 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); @@ -321,6 +323,8 @@ public: 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); @@ -354,6 +358,8 @@ private: static ProgramObject _baseVoxelProgram; static ProgramObject _splatVoxelProgram; static SplatLocations _splatVoxelLocations; + + static ProgramObject _voxelCursorProgram; }; /// Base class for spanner renderers; provides clipping. diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 6dfe3d77ed..ee7cf63789 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1529,6 +1529,15 @@ void VoxelMaterialSpannerTool::textureLoaded() { VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) : MetavoxelTool(editor, name, false, true) { + + QWidget* widget = new QWidget(); + widget->setLayout(_form = new QFormLayout()); + layout()->addWidget(widget); + + _form->addRow("Radius:", _radius = new QDoubleSpinBox()); + _radius->setSingleStep(0.01); + _radius->setMaximum(FLT_MAX); + _radius->setValue(0.25); } bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const { @@ -1548,24 +1557,71 @@ void VoxelBrushTool::render() { if (!Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, distance)) { return; } - _position = origin + distance * direction; - - glColor4f(1.0f, 0.0f, 0.0f, 1.0f); - - glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - - Application::getInstance()->getGeometryCache()->renderSphere(0.1f, 16, 16); - - glPopMatrix(); + Application::getInstance()->getMetavoxels()->renderVoxelCursor( + _position = origin + distance * direction, _radius->value()); } bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) { + if (event->type() == QEvent::Wheel) { + float angle = static_cast(event)->angleDelta().y(); + const float ANGLE_SCALE = 1.0f / 1000.0f; + _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE)); + return true; + + } else if (event->type() == QEvent::MouseButtonPress) { + MetavoxelEditMessage message = { createEdit(static_cast(event)->button() == Qt::RightButton) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); + return true; + } return false; } VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) : VoxelBrushTool(editor, "Material Brush") { + + _form->addRow("Color:", _color = new QColorEditor(this)); + connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialBrushTool::clearTexture); + _form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false)); + connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBrushTool::updateTexture); } +QVariant VoxelMaterialBrushTool::createEdit(bool alternate) { + if (alternate) { + return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); + } else { + SharedObjectPointer material = _materialEditor->getObject(); + if (static_cast(material.data())->getDiffuse().isValid()) { + _materialEditor->detachObject(); + } else { + material = SharedObjectPointer(); + } + return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), material, _color->getColor())); + } +} +void VoxelMaterialBrushTool::clearTexture() { + _materialEditor->setObject(new MaterialObject()); +} + +void VoxelMaterialBrushTool::updateTexture() { + if (_texture) { + _texture->disconnect(this); + } + MaterialObject* material = static_cast(_materialEditor->getObject().data()); + if (!material->getDiffuse().isValid()) { + _texture.clear(); + return; + } + _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); + if (_texture) { + if (_texture->isLoaded()) { + textureLoaded(); + } else { + connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialBrushTool::textureLoaded); + } + } +} + +void VoxelMaterialBrushTool::textureLoaded() { + _color->setColor(_texture->getAverageColor()); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 725eac87f7..debe45ea2f 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -485,6 +485,11 @@ public: protected: + virtual QVariant createEdit(bool alternate) = 0; + + QFormLayout* _form; + QDoubleSpinBox* _radius; + glm::vec3 _position; }; @@ -495,6 +500,22 @@ class VoxelMaterialBrushTool : public VoxelBrushTool { public: VoxelMaterialBrushTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(bool alternate); + +private slots: + + void clearTexture(); + void updateTexture(); + void textureLoaded(); + +private: + + QColorEditor* _color; + SharedObjectEditor* _materialEditor; + QSharedPointer _texture; }; #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 1a18079257..29fbe182d3 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -848,3 +848,108 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject VoxelMaterialSpannerEditVisitor visitor(spanner, material, averageColor); data.guide(visitor); } + +PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& averageColor) : + position(position), + radius(radius), + material(material), + averageColor(averageColor) { +} + +class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor { +public: + + PaintVoxelMaterialEditVisitor(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; +}; + +PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << + AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), + _position(position), + _radius(radius), + _material(material), + _color(color) { + + glm::vec3 extents(_radius, _radius, _radius); + _bounds = Box(_position - extents, _position + extents); +} + +int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); + VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue(); + if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) { + return STOP_RECURSION; + } + QVector colorContents = colorPointer->getContents(); + QByteArray materialContents = materialPointer->getContents(); + QVector materials = materialPointer->getMaterials(); + + Box overlap = info.getBounds().getIntersection(_bounds); + int size = colorPointer->getSize(); + int area = size * size; + float scale = (size - 1.0f) / info.size; + overlap.minimum = (overlap.minimum - info.minimum) * scale; + overlap.maximum = (overlap.maximum - info.minimum) * scale; + int minX = glm::ceil(overlap.minimum.x); + int minY = glm::ceil(overlap.minimum.y); + int minZ = glm::ceil(overlap.minimum.z); + int sizeX = (int)overlap.maximum.x - minX + 1; + int sizeY = (int)overlap.maximum.y - minY + 1; + int sizeZ = (int)overlap.maximum.z - minZ + 1; + + QRgb rgb = _color.rgba(); + float step = 1.0f / scale; + glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step); + uchar materialIndex = getMaterialIndex(_material, materials, materialContents); + QRgb* colorData = colorContents.data(); + uchar* materialData = (uchar*)materialContents.data(); + for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ; + destZ += area, position.z += step) { + position.y = info.minimum.y + minY * step; + for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) { + position.x = info.minimum.x + minX * step; + for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) { + QRgb& color = colorData[destX]; + if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) { + color = rgb; + materialData[destX] = materialIndex; + } + } + } + } + VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size)); + info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), + encodeInline(newColorPointer)); + + clearUnusedMaterials(materials, materialContents); + VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials)); + info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline(newMaterialPointer)); + + return STOP_RECURSION; +} + +void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + PaintVoxelMaterialEditVisitor visitor(position, radius, material, averageColor); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index bf59a1e691..0ac0bf59ec 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -261,4 +261,23 @@ public: DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit) +/// An edit that sets a region of a voxel material. +class PaintVoxelMaterialEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM glm::vec3 position; + STREAM float radius; + STREAM SharedObjectPointer material; + STREAM QColor averageColor; + + PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, + const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit) + #endif // hifi_MetavoxelMessages_h