mirror of
https://github.com/overte-org/overte.git
synced 2025-08-13 01:54:58 +02:00
More work on heightfields.
This commit is contained in:
parent
aeee369286
commit
d04eb6261e
4 changed files with 308 additions and 33 deletions
|
@ -254,15 +254,22 @@ void PointBuffer::render() {
|
|||
_buffer.release();
|
||||
}
|
||||
|
||||
HeightfieldBuffer::HeightfieldBuffer(const QByteArray& height, const QByteArray& color, const QByteArray& normal) :
|
||||
HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||
const QByteArray& height, const QByteArray& color) :
|
||||
_translation(translation),
|
||||
_scale(scale),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_normal(normal),
|
||||
_heightTexture(QOpenGLTexture::Target2D),
|
||||
_colorTexture(QOpenGLTexture::Target2D),
|
||||
_normalTexture(QOpenGLTexture::Target2D) {
|
||||
_colorTexture(QOpenGLTexture::Target2D) {
|
||||
}
|
||||
|
||||
class HeightfieldPoint {
|
||||
public:
|
||||
glm::vec2 textureCoord;
|
||||
glm::vec3 vertex;
|
||||
};
|
||||
|
||||
void HeightfieldBuffer::render() {
|
||||
// initialize textures, etc. on first render
|
||||
if (!_heightTexture.isCreated()) {
|
||||
|
@ -273,21 +280,116 @@ void HeightfieldBuffer::render() {
|
|||
_heightTexture.setData(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, _height.data());
|
||||
_height.clear();
|
||||
|
||||
int colorSize = glm::sqrt(_color.size() / 3);
|
||||
_colorTexture.setSize(colorSize, colorSize);
|
||||
if (!_color.isEmpty()) {
|
||||
int colorSize = glm::sqrt(_color.size() / 3);
|
||||
_colorTexture.setSize(colorSize, colorSize);
|
||||
}
|
||||
_colorTexture.setFormat(QOpenGLTexture::RGBFormat);
|
||||
_colorTexture.allocateStorage();
|
||||
_colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _color.data());
|
||||
_color.clear();
|
||||
if (!_color.isEmpty()) {
|
||||
_colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _color.data());
|
||||
_color.clear();
|
||||
|
||||
} else {
|
||||
const quint8 WHITE_COLOR[] = { 255, 255, 255 };
|
||||
_colorTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, const_cast<quint8*>(WHITE_COLOR));
|
||||
}
|
||||
}
|
||||
// create the buffer objects lazily
|
||||
int size = _heightTexture.width();
|
||||
int sizeWithSkirt = size + 2;
|
||||
int vertexCount = sizeWithSkirt * sizeWithSkirt;
|
||||
int rows = sizeWithSkirt - 1;
|
||||
int indexCount = rows * rows * 4;
|
||||
BufferPair& bufferPair = _bufferPairs[size];
|
||||
if (!bufferPair.first.isCreated()) {
|
||||
QVector<HeightfieldPoint> vertices(vertexCount);
|
||||
HeightfieldPoint* point = vertices.data();
|
||||
|
||||
int normalSize = glm::sqrt(_normal.size() / 3);
|
||||
_normalTexture.setSize(normalSize, normalSize);
|
||||
_normalTexture.setFormat(QOpenGLTexture::RGBFormat);
|
||||
_normalTexture.allocateStorage();
|
||||
_normalTexture.setData(QOpenGLTexture::BGR, QOpenGLTexture::UInt8, _normal.data());
|
||||
_normal.clear();
|
||||
float step = 1.0f / (size - 1);
|
||||
float z = -step;
|
||||
for (int i = 0; i < sizeWithSkirt; i++, z += step) {
|
||||
float x = -step;
|
||||
const float SKIRT_LENGTH = 1.0f;
|
||||
float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f;
|
||||
for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) {
|
||||
point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z);
|
||||
point->textureCoord = glm::vec2(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
bufferPair.first.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||
bufferPair.first.create();
|
||||
bufferPair.first.bind();
|
||||
bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint));
|
||||
|
||||
QVector<int> indices(indexCount);
|
||||
int* index = indices.data();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
int lineIndex = i * sizeWithSkirt;
|
||||
int nextLineIndex = (i + 1) * sizeWithSkirt;
|
||||
for (int j = 0; j < rows; j++) {
|
||||
*index++ = lineIndex + j;
|
||||
*index++ = lineIndex + j + 1;
|
||||
*index++ = nextLineIndex + j;
|
||||
*index++ = nextLineIndex + j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bufferPair.second = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
|
||||
bufferPair.second.create();
|
||||
bufferPair.second.bind();
|
||||
bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int));
|
||||
|
||||
} else {
|
||||
bufferPair.first.bind();
|
||||
bufferPair.second.bind();
|
||||
}
|
||||
|
||||
HeightfieldPoint* point = 0;
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(_translation.x, _translation.y, _translation.z);
|
||||
glScalef(_scale, _scale, _scale);
|
||||
|
||||
_heightTexture.bind(0);
|
||||
_colorTexture.bind(1);
|
||||
|
||||
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
_colorTexture.release(1);
|
||||
_heightTexture.release(0);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
bufferPair.first.release();
|
||||
bufferPair.second.release();
|
||||
}
|
||||
|
||||
QHash<int, HeightfieldBuffer::BufferPair> HeightfieldBuffer::_bufferPairs;
|
||||
|
||||
void HeightfieldPreview::render(const glm::vec3& translation, float scale) const {
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(translation.x, translation.y, translation.z);
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
foreach (const BufferDataPointer& buffer, _buffers) {
|
||||
buffer->render();
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().release();
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
BufferDataAttribute::BufferDataAttribute(const QString& name) :
|
||||
|
@ -368,11 +470,14 @@ int PointAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
|
||||
_points.append(point);
|
||||
}
|
||||
if (info.size >= _pointLeafSize) {
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(
|
||||
new PointBuffer(swapPoints))));
|
||||
if (info.size >= _pointLeafSize) {
|
||||
PointBuffer* buffer = NULL;
|
||||
if (!_points.isEmpty()) {
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
buffer = new PointBuffer(swapPoints);
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
@ -381,10 +486,13 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) {
|
|||
if (info.size != _pointLeafSize) {
|
||||
return false;
|
||||
}
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(
|
||||
new PointBuffer(swapPoints))));
|
||||
PointBuffer* buffer = NULL;
|
||||
if (!_points.isEmpty()) {
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
buffer = new PointBuffer(swapPoints);
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -403,7 +511,18 @@ HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) :
|
|||
}
|
||||
|
||||
int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||
return STOP_RECURSION;
|
||||
if (info.isLeaf) {
|
||||
HeightfieldBuffer* buffer = NULL;
|
||||
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (height) {
|
||||
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, height->getContents(),
|
||||
color ? color->getContents() : QByteArray());
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
|
||||
void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
|
||||
|
@ -419,11 +538,19 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const
|
|||
data.setRoot(pointBufferAttribute, root);
|
||||
root->incrementReferenceCount();
|
||||
}
|
||||
const AttributePointer& heightfieldBufferAttribute =
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute();
|
||||
root = expandedPrevious.getRoot(heightfieldBufferAttribute);
|
||||
if (root) {
|
||||
data.setRoot(heightfieldBufferAttribute, root);
|
||||
root->incrementReferenceCount();
|
||||
}
|
||||
|
||||
PointAugmentVisitor visitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, visitor);
|
||||
|
||||
PointAugmentVisitor pointAugmentVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, pointAugmentVisitor);
|
||||
|
||||
HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor);
|
||||
}
|
||||
|
||||
class SpannerSimulateVisitor : public SpannerVisitor {
|
||||
|
@ -530,16 +657,24 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
|||
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
_pointProgram.release();
|
||||
|
||||
_heightfieldProgram.bind();
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
||||
lod);
|
||||
data.guide(heightfieldRenderVisitor);
|
||||
|
||||
_heightfieldProgram.release();
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
|
|
|
@ -125,18 +125,34 @@ private:
|
|||
class HeightfieldBuffer : public BufferData {
|
||||
public:
|
||||
|
||||
HeightfieldBuffer(const QByteArray& height, const QByteArray& color, const QByteArray& normal);
|
||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color);
|
||||
|
||||
virtual void render();
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _translation;
|
||||
float _scale;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
QByteArray _normal;
|
||||
QOpenGLTexture _heightTexture;
|
||||
QOpenGLTexture _colorTexture;
|
||||
QOpenGLTexture _normalTexture;
|
||||
|
||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||
static QHash<int, BufferPair> _bufferPairs;
|
||||
};
|
||||
|
||||
/// Convenience class for rendering a preview of a heightfield.
|
||||
class HeightfieldPreview {
|
||||
public:
|
||||
|
||||
void setBuffers(const QVector<BufferDataPointer>& buffers) { _buffers = buffers; }
|
||||
|
||||
void render(const glm::vec3& translation, float scale) const;
|
||||
|
||||
private:
|
||||
|
||||
QVector<BufferDataPointer> _buffers;
|
||||
};
|
||||
|
||||
/// A client-side attribute that stores renderable buffers.
|
||||
|
@ -157,7 +173,9 @@ class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplement
|
|||
public:
|
||||
|
||||
static void init();
|
||||
|
||||
|
||||
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
||||
|
||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||
|
||||
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMetaProperty>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaProperty>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QPushButton>
|
||||
|
@ -113,6 +114,7 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
addTool(new RemoveSpannerTool(this));
|
||||
addTool(new ClearSpannersTool(this));
|
||||
addTool(new SetSpannerTool(this));
|
||||
addTool(new ImportHeightfieldTool(this));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
|
@ -891,3 +893,92 @@ void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedOb
|
|||
QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds,
|
||||
spannerData->getVoxelizationGranularity(), directionImages));
|
||||
}
|
||||
|
||||
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
||||
MetavoxelTool(editor, "Import Heightfield", false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
QFormLayout* form = new QFormLayout();
|
||||
widget->setLayout(form);
|
||||
layout()->addWidget(widget);
|
||||
|
||||
form->addRow("Height:", _height = new QPushButton());
|
||||
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
|
||||
form->addRow("Color:", _color = new QPushButton());
|
||||
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
|
||||
}
|
||||
|
||||
bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("HeightfieldAttribute");
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::render() {
|
||||
_preview.render(glm::vec3(), 1.0f);
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::selectHeightFile() {
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(), "Images (*.png *.jpg)");
|
||||
if (filename.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!_heightImage.load(filename)) {
|
||||
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
|
||||
return;
|
||||
}
|
||||
_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)");
|
||||
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();
|
||||
}
|
||||
|
||||
const int BLOCK_SIZE = 64;
|
||||
|
||||
void ImportHeightfieldTool::updatePreview() {
|
||||
QVector<BufferDataPointer> buffers;
|
||||
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
|
||||
float z = 0.0f;
|
||||
for (int i = 0; i < _heightImage.height(); i += BLOCK_SIZE, z++) {
|
||||
float x = 0.0f;
|
||||
for (int j = 0; j < _heightImage.width(); j += BLOCK_SIZE, x++) {
|
||||
QByteArray height(BLOCK_SIZE * BLOCK_SIZE, 0);
|
||||
int rows = qMin(BLOCK_SIZE, _heightImage.height() - i);
|
||||
int columns = qMin(BLOCK_SIZE, _heightImage.width() - j);
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
for (int y = 0; y < rows; y++) {
|
||||
uchar* src = _heightImage.scanLine(i + y);
|
||||
char* dest = height.data() + y * BLOCK_SIZE;
|
||||
for (int x = 0; x < columns; x++) {
|
||||
*dest++ = *src;
|
||||
src += BYTES_PER_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray color;
|
||||
if (!_colorImage.isNull()) {
|
||||
color = QByteArray(BLOCK_SIZE * BLOCK_SIZE * BYTES_PER_COLOR, 0);
|
||||
rows = qMax(0, qMin(BLOCK_SIZE, _colorImage.height() - i));
|
||||
columns = qMax(0, qMin(BLOCK_SIZE, _colorImage.width() - j));
|
||||
for (int y = 0; y < rows; y++) {
|
||||
memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR, _colorImage.scanLine(i + y),
|
||||
columns * BYTES_PER_COLOR);
|
||||
}
|
||||
}
|
||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
|
||||
}
|
||||
}
|
||||
}
|
||||
_preview.setBuffers(buffers);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QList>
|
||||
#include <QWidget>
|
||||
|
||||
#include "MetavoxelSystem.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
class QComboBox;
|
||||
|
@ -223,4 +224,34 @@ protected:
|
|||
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
};
|
||||
|
||||
/// Allows importing a heightfield.
|
||||
class ImportHeightfieldTool : public MetavoxelTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ImportHeightfieldTool(MetavoxelEditor* editor);
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
virtual void render();
|
||||
|
||||
private slots:
|
||||
|
||||
void selectHeightFile();
|
||||
void selectColorFile();
|
||||
|
||||
private:
|
||||
|
||||
void updatePreview();
|
||||
|
||||
QPushButton* _height;
|
||||
QPushButton* _color;
|
||||
|
||||
QImage _heightImage;
|
||||
QImage _colorImage;
|
||||
|
||||
HeightfieldPreview _preview;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelEditor_h
|
||||
|
|
Loading…
Reference in a new issue