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)); }