mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-10 14:47:22 +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;
|
uniform sampler2D heightMap;
|
||||||
|
|
||||||
// the distance between height points in texture space
|
// the distance between height points in texture space
|
||||||
uniform vec3 heightScale;
|
uniform vec4 heightScale;
|
||||||
|
|
||||||
// the scale between height and color textures
|
// the scale between height and color textures
|
||||||
uniform vec2 colorScale;
|
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,
|
||||||
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);
|
vec4 neighborsZero = step(1.0 / 65535.0, neighborHeights);
|
||||||
normal = normalize(gl_ModelViewMatrix * vec4(
|
normal = vec4(normalize(gl_NormalMatrix * vec3(
|
||||||
heightScale.s * (neighborHeights.x - neighborHeights.y) * neighborsZero.x * neighborsZero.y, heightScale.p,
|
heightScale.p * (neighborHeights.y - neighborHeights.x) * neighborsZero.x * neighborsZero.y, 1.0,
|
||||||
heightScale.t * (neighborHeights.z - neighborHeights.w) * neighborsZero.z * neighborsZero.w, 0.0));
|
heightScale.q * (neighborHeights.w - neighborHeights.z) * neighborsZero.z * neighborsZero.w)), 0.0);
|
||||||
|
|
||||||
// add the height to the position
|
// add the height to the position
|
||||||
float height = texture2D(heightMap, heightCoord).r;
|
float height = texture2D(heightMap, heightCoord).r;
|
||||||
|
|
|
@ -18,10 +18,10 @@ uniform sampler2D heightMap;
|
||||||
uniform sampler2D textureMap;
|
uniform sampler2D textureMap;
|
||||||
|
|
||||||
// the distance between height points in texture space
|
// the distance between height points in texture space
|
||||||
uniform float heightScale;
|
uniform vec2 heightScale;
|
||||||
|
|
||||||
// the scale between height and texture textures
|
// the scale between height and texture textures
|
||||||
uniform float textureScale;
|
uniform vec2 textureScale;
|
||||||
|
|
||||||
// the splat texture offset
|
// the splat texture offset
|
||||||
uniform vec2 splatTextureOffset;
|
uniform vec2 splatTextureOffset;
|
||||||
|
@ -58,7 +58,7 @@ void main(void) {
|
||||||
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
|
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
|
||||||
|
|
||||||
// compute the alpha values for each texture
|
// 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);
|
vec4 valueVector = vec4(value, value, value, value);
|
||||||
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
|
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);
|
glm::vec3 axis = glm::axis(rotation);
|
||||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
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();
|
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);
|
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||||
glScalef(1.0f, cuboid->getAspectY(), cuboid->getAspectZ());
|
glScalef(1.0f, cuboid->getAspectY(), cuboid->getAspectZ());
|
||||||
|
|
||||||
glutSolidCube(cuboid->getScale() * 2.0f);
|
Application::getInstance()->getDeferredLightingEffect()->renderSolidCube(cuboid->getScale() * 2.0f);
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
@ -3038,12 +3038,12 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) {
|
||||||
float xStep = 1.0f / (innerWidth - 1);
|
float xStep = 1.0f / (innerWidth - 1);
|
||||||
float zStep = 1.0f / (innerHeight - 1);
|
float zStep = 1.0f / (innerHeight - 1);
|
||||||
float z = -zStep;
|
float z = -zStep;
|
||||||
float sStep = 1.0f / innerWidth;
|
float sStep = 1.0f / width;
|
||||||
float tStep = 1.0f / innerHeight;
|
float tStep = 1.0f / height;
|
||||||
float t = -tStep / 2.0f;
|
float t = tStep / 2.0f;
|
||||||
for (int i = 0; i < height; i++, z += zStep, t += tStep) {
|
for (int i = 0; i < height; i++, z += zStep, t += tStep) {
|
||||||
float x = -xStep;
|
float x = -xStep;
|
||||||
float s = -sStep / 2.0f;
|
float s = sStep / 2.0f;
|
||||||
const float SKIRT_LENGTH = 0.25f;
|
const float SKIRT_LENGTH = 0.25f;
|
||||||
float baseY = (i == 0 || i == height - 1) ? -SKIRT_LENGTH : 0.0f;
|
float baseY = (i == 0 || i == height - 1) ? -SKIRT_LENGTH : 0.0f;
|
||||||
for (int j = 0; j < width; j++, point++, x += xStep, s += sStep) {
|
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);
|
glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z);
|
||||||
glm::vec3 axis = glm::axis(heightfield->getRotation());
|
glm::vec3 axis = glm::axis(heightfield->getRotation());
|
||||||
glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z);
|
glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z);
|
||||||
glScalef(heightfield->getScale(), heightfield->getScale() * heightfield->getAspectY(),
|
float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ();
|
||||||
heightfield->getScale() * heightfield->getAspectZ());
|
glScalef(xScale, xScale * heightfield->getAspectY(), zScale);
|
||||||
|
|
||||||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true);
|
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);
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind();
|
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind();
|
||||||
|
float sDistance = 2.0f / (innerWidth - 1);
|
||||||
|
float tDistance = 2.0f / (innerHeight - 1);
|
||||||
|
float distanceProduct = -sDistance * tDistance;
|
||||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue(
|
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue(
|
||||||
DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / innerWidth, 1.0f / innerHeight,
|
DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / width, 1.0f / height,
|
||||||
2.0f / (innerWidth * innerHeight));
|
sDistance / distanceProduct, tDistance / distanceProduct);
|
||||||
if (heightfield->getColor()) {
|
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue(
|
||||||
int colorWidth = heightfield->getColor()->getWidth();
|
DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)width / innerWidth,
|
||||||
int colorHeight = heightfield->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES);
|
(float)height / innerHeight);
|
||||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue(
|
|
||||||
DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)innerWidth / colorWidth,
|
|
||||||
(float)innerHeight / colorHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
@ -3127,19 +3126,93 @@ void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) {
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false);
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release();
|
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release();
|
||||||
|
|
||||||
glDisable(GL_ALPHA_TEST);
|
glDisable(GL_ALPHA_TEST);
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
glEnable(GL_BLEND);
|
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_TEXTURE_COORD_ARRAY);
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true);
|
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
bufferPair.first.release();
|
bufferPair.first.release();
|
||||||
|
|
|
@ -125,7 +125,6 @@ MetavoxelEditor::MetavoxelEditor() :
|
||||||
addTool(new HeightfieldHeightBrushTool(this));
|
addTool(new HeightfieldHeightBrushTool(this));
|
||||||
addTool(new HeightfieldMaterialBrushTool(this));
|
addTool(new HeightfieldMaterialBrushTool(this));
|
||||||
addTool(new ImportHeightfieldTool(this));
|
addTool(new ImportHeightfieldTool(this));
|
||||||
addTool(new EraseHeightfieldTool(this));
|
|
||||||
addTool(new VoxelMaterialBoxTool(this));
|
addTool(new VoxelMaterialBoxTool(this));
|
||||||
addTool(new VoxelMaterialSpannerTool(this));
|
addTool(new VoxelMaterialSpannerTool(this));
|
||||||
addTool(new VoxelMaterialBrushTool(this));
|
addTool(new VoxelMaterialBrushTool(this));
|
||||||
|
@ -606,6 +605,7 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name,
|
||||||
box->setContentsMargins(QMargins());
|
box->setContentsMargins(QMargins());
|
||||||
box->addStretch(1);
|
box->addStretch(1);
|
||||||
box->addWidget(_followMouse = new QCheckBox("Follow Mouse"));
|
box->addWidget(_followMouse = new QCheckBox("Follow Mouse"));
|
||||||
|
_followMouse->setChecked(true);
|
||||||
box->addStretch(1);
|
box->addStretch(1);
|
||||||
|
|
||||||
if (!placeText.isEmpty()) {
|
if (!placeText.isEmpty()) {
|
||||||
|
@ -616,12 +616,9 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name,
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaceSpannerTool::simulate(float deltaTime) {
|
void PlaceSpannerTool::simulate(float deltaTime) {
|
||||||
if (!Application::getInstance()->getGLWidget()->hasFocus() || Application::getInstance()->isMouseHidden()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Spanner* spanner = static_cast<Spanner*>(getSpanner(true).data());
|
Spanner* spanner = static_cast<Spanner*>(getSpanner(true).data());
|
||||||
Transformable* transformable = qobject_cast<Transformable*>(spanner);
|
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
|
// find the intersection of the mouse ray with the grid and place the transformable there
|
||||||
glm::quat rotation = _editor->getGridRotation();
|
glm::quat rotation = _editor->getGridRotation();
|
||||||
glm::quat inverseRotation = glm::inverse(rotation);
|
glm::quat inverseRotation = glm::inverse(rotation);
|
||||||
|
@ -741,7 +738,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
|
||||||
_scale->setMinimum(-FLT_MAX);
|
_scale->setMinimum(-FLT_MAX);
|
||||||
_scale->setMaximum(FLT_MAX);
|
_scale->setMaximum(FLT_MAX);
|
||||||
_scale->setPrefix("2^");
|
_scale->setPrefix("2^");
|
||||||
_scale->setValue(3.0);
|
_scale->setValue(2.0);
|
||||||
|
|
||||||
QPushButton* applyButton = new QPushButton("Apply");
|
QPushButton* applyButton = new QPushButton("Apply");
|
||||||
layout()->addWidget(applyButton);
|
layout()->addWidget(applyButton);
|
||||||
|
@ -749,7 +746,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const {
|
bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const {
|
||||||
return attribute->inherits("HeightfieldAttribute");
|
return attribute->inherits("SpannerSetAttribute");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldTool::render() {
|
void HeightfieldTool::render() {
|
||||||
|
@ -761,269 +758,62 @@ void HeightfieldTool::render() {
|
||||||
|
|
||||||
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
||||||
HeightfieldTool(editor, "Import Heightfield"),
|
HeightfieldTool(editor, "Import Heightfield"),
|
||||||
_loadingImage(false) {
|
_spanner(new Heightfield()) {
|
||||||
|
|
||||||
_form->addRow("Block Size:", _blockSize = new QSpinBox());
|
_form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox());
|
||||||
_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());
|
|
||||||
const double MAX_OFFSET_SCALE = 100000.0;
|
const double MAX_OFFSET_SCALE = 100000.0;
|
||||||
_heightScale->setMaximum(MAX_OFFSET_SCALE);
|
_heightScale->setMaximum(MAX_OFFSET_SCALE);
|
||||||
_heightScale->setSingleStep(0.0001);
|
_heightScale->setDecimals(3);
|
||||||
_heightScale->setDecimals(4);
|
_heightScale->setSingleStep(0.001);
|
||||||
_heightScale->setValue(1.0);
|
_heightScale->setValue(0.05);
|
||||||
connect(_heightScale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
|
connect(_heightScale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
|
||||||
&ImportHeightfieldTool::updateHeightImage);
|
&ImportHeightfieldTool::updateSpanner);
|
||||||
|
|
||||||
rawLayout->addSpacing(15);
|
_form->addRow("Height Offset:", _heightOffset = new QDoubleSpinBox());
|
||||||
rawLayout->addWidget(new QLabel("Offset:"));
|
|
||||||
rawLayout->addWidget(_heightOffset = new QDoubleSpinBox());
|
|
||||||
_heightOffset->setMinimum(-MAX_OFFSET_SCALE);
|
_heightOffset->setMinimum(-MAX_OFFSET_SCALE);
|
||||||
_heightOffset->setMaximum(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,
|
connect(_heightOffset, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
|
||||||
&ImportHeightfieldTool::updateHeightImage);
|
&ImportHeightfieldTool::updateSpanner);
|
||||||
|
|
||||||
_form->addRow("Color:", _color = new QPushButton());
|
_form->addRow("Height:", _height = new HeightfieldHeightEditor(this));
|
||||||
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
|
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() {
|
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() {
|
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() {
|
void ImportHeightfieldTool::updateSpanner() {
|
||||||
QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(),
|
Heightfield* heightfield = static_cast<Heightfield*>(_spanner.data());
|
||||||
"Images (*.png *.jpg *.bmp *.raw)");
|
heightfield->setHeight(_height->getHeight());
|
||||||
if (filename.isNull()) {
|
heightfield->setColor(_color->getColor());
|
||||||
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") {
|
|
||||||
|
|
||||||
_form->addRow("Width:", _width = new QSpinBox());
|
float scale = pow(2.0, _scale->value());
|
||||||
_width->setMinimum(1);
|
if (_height->getHeight()) {
|
||||||
_width->setMaximum(INT_MAX);
|
int innerSize = _height->getHeight()->getWidth() - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||||
_form->addRow("Length:", _length = new QSpinBox());
|
scale *= glm::ceil((float)innerSize / HEIGHTFIELD_BLOCK_SIZE);
|
||||||
_length->setMinimum(1);
|
}
|
||||||
_length->setMaximum(INT_MAX);
|
heightfield->setScale(scale);
|
||||||
}
|
heightfield->setAspectY(_heightScale->value());
|
||||||
|
heightfield->setTranslation(_translation->getValue() + glm::vec3(0.0f, _heightOffset->value(), 0.0f));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
|
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
|
||||||
|
|
|
@ -281,6 +281,8 @@ public:
|
||||||
|
|
||||||
ImportHeightfieldTool(MetavoxelEditor* editor);
|
ImportHeightfieldTool(MetavoxelEditor* editor);
|
||||||
|
|
||||||
|
virtual void simulate(float deltaTime);
|
||||||
|
|
||||||
virtual void renderPreview();
|
virtual void renderPreview();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -289,48 +291,17 @@ protected:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void selectHeightFile();
|
void updateSpanner();
|
||||||
void selectColorFile();
|
|
||||||
void updateHeightImage();
|
|
||||||
void updatePreview();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QSpinBox* _blockSize;
|
|
||||||
|
|
||||||
QPushButton* _height;
|
|
||||||
QWidget* _rawOptions;
|
|
||||||
QDoubleSpinBox* _heightScale;
|
QDoubleSpinBox* _heightScale;
|
||||||
QDoubleSpinBox* _heightOffset;
|
QDoubleSpinBox* _heightOffset;
|
||||||
bool _loadingImage;
|
|
||||||
|
|
||||||
QPushButton* _color;
|
HeightfieldHeightEditor* _height;
|
||||||
|
HeightfieldColorEditor* _color;
|
||||||
|
|
||||||
QVector<quint16> _rawHeight;
|
SharedObjectPointer _spanner;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for tools that allow painting on heightfields.
|
/// Base class for tools that allow painting on heightfields.
|
||||||
|
|
|
@ -59,7 +59,6 @@ AttributeRegistry::AttributeRegistry() :
|
||||||
|
|
||||||
const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f;
|
const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f;
|
||||||
_heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
_heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
||||||
_heightfieldAttribute->setUserFacing(true);
|
|
||||||
_heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
_heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
||||||
_heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
_heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,10 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
REGISTER_META_OBJECT(Spanner)
|
REGISTER_META_OBJECT(Spanner)
|
||||||
|
REGISTER_META_OBJECT(Heightfield)
|
||||||
REGISTER_META_OBJECT(Sphere)
|
REGISTER_META_OBJECT(Sphere)
|
||||||
REGISTER_META_OBJECT(Cuboid)
|
REGISTER_META_OBJECT(Cuboid)
|
||||||
REGISTER_META_OBJECT(StaticModel)
|
REGISTER_META_OBJECT(StaticModel)
|
||||||
REGISTER_META_OBJECT(Heightfield)
|
|
||||||
|
|
||||||
static int heightfieldHeightTypeId = registerSimpleMetaType<HeightfieldHeightPointer>();
|
static int heightfieldHeightTypeId = registerSimpleMetaType<HeightfieldHeightPointer>();
|
||||||
static int heightfieldColorTypeId = registerSimpleMetaType<HeightfieldColorPointer>();
|
static int heightfieldColorTypeId = registerSimpleMetaType<HeightfieldColorPointer>();
|
||||||
|
@ -604,6 +604,33 @@ HeightfieldData::HeightfieldData(int width) :
|
||||||
_width(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) :
|
HeightfieldHeight::HeightfieldHeight(int width, const QVector<quint16>& contents) :
|
||||||
HeightfieldData(width),
|
HeightfieldData(width),
|
||||||
_contents(contents) {
|
_contents(contents) {
|
||||||
|
@ -628,7 +655,7 @@ HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const Heightfield
|
||||||
void HeightfieldHeight::write(Bitstream& out) {
|
void HeightfieldHeight::write(Bitstream& out) {
|
||||||
QMutexLocker locker(&_encodedMutex);
|
QMutexLocker locker(&_encodedMutex);
|
||||||
if (_encoded.isEmpty()) {
|
if (_encoded.isEmpty()) {
|
||||||
|
_encoded = encodeHeightfieldHeight(0, 0, _width, _contents.size() / _width, _contents);
|
||||||
}
|
}
|
||||||
out << _encoded.size();
|
out << _encoded.size();
|
||||||
out.writeAligned(_encoded);
|
out.writeAligned(_encoded);
|
||||||
|
@ -648,6 +675,8 @@ void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointe
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldHeight::read(Bitstream& in, int bytes) {
|
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) {
|
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) {
|
||||||
|
|
|
@ -356,6 +356,8 @@ public:
|
||||||
|
|
||||||
HeightfieldHeightEditor(QWidget* parent = NULL);
|
HeightfieldHeightEditor(QWidget* parent = NULL);
|
||||||
|
|
||||||
|
const HeightfieldHeightPointer& getHeight() const { return _height; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void heightChanged(const HeightfieldHeightPointer& height);
|
void heightChanged(const HeightfieldHeightPointer& height);
|
||||||
|
@ -416,6 +418,8 @@ public:
|
||||||
|
|
||||||
HeightfieldColorEditor(QWidget* parent = NULL);
|
HeightfieldColorEditor(QWidget* parent = NULL);
|
||||||
|
|
||||||
|
const HeightfieldColorPointer& getColor() const { return _color; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void colorChanged(const HeightfieldColorPointer& color);
|
void colorChanged(const HeightfieldColorPointer& color);
|
||||||
|
|
Loading…
Reference in a new issue