Yet more heightfield spanner bits.

This commit is contained in:
Andrzej Kapolka 2014-11-12 15:40:54 -08:00
parent 16bf2a3ad3
commit d05bb49a74
8 changed files with 186 additions and 320 deletions

View file

@ -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;

View file

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

View file

@ -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<SharedObjectPointer>& 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<MaterialObject*>(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();

View file

@ -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<Spanner*>(getSpanner(true).data());
Transformable* transformable = qobject_cast<Transformable*>(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<void (QSpinBox::*)(int)>(&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<void (QDoubleSpinBox::*)(double)>(&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<void (QDoubleSpinBox::*)(double)>(&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<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
&ImportHeightfieldTool::updateSpanner);
}
void ImportHeightfieldTool::simulate(float deltaTime) {
static_cast<Heightfield*>(_spanner.data())->getRenderer()->simulate(deltaTime);
}
void ImportHeightfieldTool::renderPreview() {
_preview.render(_translation->getValue(), _translation->getSingleStep());
static_cast<Heightfield*>(_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<HeightfieldBuffer*>(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<quint16>::max();
int maxHeight = numeric_limits<quint16>::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<BufferDataPointer> 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<Heightfield*>(_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) :

View file

@ -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<quint16> _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.

View file

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

View file

@ -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<HeightfieldHeightPointer>();
static int heightfieldColorTypeId = registerSimpleMetaType<HeightfieldColorPointer>();
@ -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<quint16>& 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<quint16> 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<quint16> decoded(payloadSize / sizeof(quint16));
memcpy(decoded.data(), inflated.constData() + HEIGHTFIELD_HEIGHT_HEADER_SIZE, payloadSize);
return decoded;
}
HeightfieldHeight::HeightfieldHeight(int width, const QVector<quint16>& 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) {

View file

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