mirror of
https://github.com/overte-org/overte.git
synced 2025-04-17 11:20:42 +02:00
Yet more heightfield spanner bits.
This commit is contained in:
parent
16bf2a3ad3
commit
d05bb49a74
8 changed files with 186 additions and 320 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue