From d05bb49a7417fb2af7cdca88a6838c3eaffbaab2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Nov 2014 15:40:54 -0800 Subject: [PATCH] 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);