More work on heightfield spanners.

This commit is contained in:
Andrzej Kapolka 2014-11-10 18:06:38 -08:00
parent 0664ff3181
commit b94dab0e8f
4 changed files with 263 additions and 24 deletions

View file

@ -14,7 +14,6 @@
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QItemEditorCreatorBase>
#include <QItemEditorFactory>
#include <QLineEdit>
#include <QMetaType>
@ -77,32 +76,11 @@ QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const {
return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName;
}
static QItemEditorFactory* getItemEditorFactory() {
QItemEditorFactory* getItemEditorFactory() {
static QItemEditorFactory* factory = new DelegatingItemEditorFactory();
return factory;
}
/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create,
/// this class simply delays the value property name lookup until actually requested.
template<class T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name();
}
return _valuePropertyName;
}
static QItemEditorCreatorBase* createDoubleEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<DoubleEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<double>(), creator);

View file

@ -14,6 +14,7 @@
#include <QColor>
#include <QComboBox>
#include <QItemEditorCreatorBase>
#include <QSharedPointer>
#include <QUrl>
#include <QWidget>
@ -24,6 +25,7 @@
class QByteArray;
class QDoubleSpinBox;
class QItemEditorFactory;
class QPushButton;
class NetworkProgram;
@ -108,6 +110,30 @@ private:
AxisExtents _crossProductExtents[CROSS_PRODUCT_EXTENT_COUNT];
};
/// Returns a pointer to the singleton item editor factory.
QItemEditorFactory* getItemEditorFactory();
/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create,
/// this class simply delays the value property name lookup until actually requested.
template<class T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name();
}
return _valuePropertyName;
}
/// Editor for meta-object values.
class QMetaObjectEditor : public QWidget {
Q_OBJECT

View file

@ -9,6 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <limits>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QItemEditorFactory>
#include <QMessageBox>
#include <QPushButton>
#include <QSettings>
#include <QThread>
#include <glm/gtx/transform.hpp>
@ -17,6 +25,8 @@
#include "Spanner.h"
using namespace std;
REGISTER_META_OBJECT(Spanner)
REGISTER_META_OBJECT(Sphere)
REGISTER_META_OBJECT(Cuboid)
@ -27,6 +37,21 @@ static int heightfieldHeightTypeId = registerSimpleMetaType<HeightfieldHeightPoi
static int heightfieldColorTypeId = registerSimpleMetaType<HeightfieldColorPointer>();
static int heightfieldMaterialTypeId = registerSimpleMetaType<HeightfieldMaterialPointer>();
static QItemEditorCreatorBase* createHeightfieldHeightEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<HeightfieldHeightEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<HeightfieldHeightPointer>(), creator);
return creator;
}
static QItemEditorCreatorBase* createHeightfieldColorEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<HeightfieldColorEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<HeightfieldColorPointer>(), creator);
return creator;
}
static QItemEditorCreatorBase* heightfieldHeightEditorCreator = createHeightfieldHeightEditorCreator();
static QItemEditorCreatorBase* heightfieldColorEditorCreator = createHeightfieldColorEditorCreator();
const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f;
const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f);
@ -627,6 +652,99 @@ template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value,
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
}
HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) :
QWidget(parent) {
QHBoxLayout* layout = new QHBoxLayout();
setLayout(layout);
layout->addWidget(_select = new QPushButton("Select"));
connect(_select, &QPushButton::clicked, this, &HeightfieldHeightEditor::select);
layout->addWidget(_clear = new QPushButton("Clear"));
connect(_clear, &QPushButton::clicked, this, &HeightfieldHeightEditor::clear);
_clear->setEnabled(false);
}
void HeightfieldHeightEditor::setHeight(const HeightfieldHeightPointer& height) {
if ((_height = height)) {
_clear->setEnabled(true);
} else {
_clear->setEnabled(false);
}
}
static int getHeightfieldSize(int size) {
return (int)glm::pow(2.0f, glm::round(glm::log((float)size - HeightfieldData::SHARED_EDGE) /
glm::log(2.0f))) + HeightfieldData::SHARED_EDGE;
}
void HeightfieldHeightEditor::select() {
QSettings settings;
QString result = QFileDialog::getOpenFileName(this, "Select Height Image", settings.value("heightDir").toString(),
"Images (*.png *.jpg *.bmp *.raw)");
if (result.isNull()) {
return;
}
settings.setValue("heightDir", QFileInfo(result).path());
QImage image;
if (!image.load(result)) {
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
return;
}
const quint16 CONVERSION_OFFSET = 1;
if (result.toLower().endsWith(".raw")) {
QFile input(result);
input.open(QIODevice::ReadOnly);
QDataStream in(&input);
in.setByteOrder(QDataStream::LittleEndian);
QVector<quint16> rawContents;
while (!in.atEnd()) {
quint16 height;
in >> height;
rawContents.append(height);
}
if (rawContents.isEmpty()) {
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
return;
}
int rawSize = glm::sqrt((float)rawContents.size());
int size = getHeightfieldSize(rawSize) + 2 * HeightfieldHeight::HEIGHT_BORDER;
QVector<quint16> contents(size * size);
quint16* dest = contents.data() + (size + 1) * HeightfieldHeight::HEIGHT_BORDER;
const quint16* src = rawContents.constData();
const float CONVERSION_SCALE = 65534.0f / numeric_limits<quint16>::max();
for (int i = 0; i < rawSize; i++, dest += size) {
for (quint16* lineDest = dest, *end = dest + rawSize; lineDest != end; lineDest++, src++) {
*lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET;
}
}
emit heightChanged(_height = new HeightfieldHeight(size, contents));
_clear->setEnabled(true);
return;
}
image = image.convertToFormat(QImage::Format_RGB888);
int width = getHeightfieldSize(image.width()) + 2 * HeightfieldHeight::HEIGHT_BORDER;
int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER;
QVector<quint16> contents(width * height);
quint16* dest = contents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER;
const float CONVERSION_SCALE = 65534.0f / EIGHT_BIT_MAXIMUM;
for (int i = 0; i < image.height(); i++, dest += width) {
const uchar* src = image.constScanLine(i);
for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++,
src += DataBlock::COLOR_BYTES) {
*lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET;
}
}
emit heightChanged(_height = new HeightfieldHeight(width, contents));
_clear->setEnabled(true);
}
void HeightfieldHeightEditor::clear() {
emit heightChanged(_height = HeightfieldHeightPointer());
_clear->setEnabled(false);
}
HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) :
HeightfieldData(width),
_contents(contents) {
@ -677,6 +795,58 @@ template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, c
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
}
HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) :
QWidget(parent) {
QHBoxLayout* layout = new QHBoxLayout();
setLayout(layout);
layout->addWidget(_select = new QPushButton("Select"));
connect(_select, &QPushButton::clicked, this, &HeightfieldColorEditor::select);
layout->addWidget(_clear = new QPushButton("Clear"));
connect(_clear, &QPushButton::clicked, this, &HeightfieldColorEditor::clear);
_clear->setEnabled(false);
}
void HeightfieldColorEditor::setColor(const HeightfieldColorPointer& color) {
if ((_color = color)) {
_clear->setEnabled(true);
} else {
_clear->setEnabled(false);
}
}
void HeightfieldColorEditor::select() {
QSettings settings;
QString result = QFileDialog::getOpenFileName(this, "Select Color Image", settings.value("heightDir").toString(),
"Images (*.png *.jpg *.bmp)");
if (result.isNull()) {
return;
}
settings.setValue("heightDir", QFileInfo(result).path());
QImage image;
if (!image.load(result)) {
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
return;
}
image = image.convertToFormat(QImage::Format_RGB888);
int width = getHeightfieldSize(image.width());
int height = getHeightfieldSize(image.height());
QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0);
char* dest = contents.data();
for (int i = 0; i < image.height(); i++, dest += width * DataBlock::COLOR_BYTES) {
memcpy(dest, image.constScanLine(i), image.width() * DataBlock::COLOR_BYTES);
}
emit colorChanged(_color = new HeightfieldColor(width, contents));
_clear->setEnabled(true);
}
void HeightfieldColorEditor::clear() {
emit colorChanged(_color = HeightfieldColorPointer());
_clear->setEnabled(false);
}
HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents,
const QVector<SharedObjectPointer>& materials) :
HeightfieldData(width),

View file

@ -303,6 +303,8 @@ private:
class HeightfieldData : public DataBlock {
public:
static const int SHARED_EDGE = 1;
HeightfieldData(int width = 0);
int getWidth() const { return _width; }
@ -318,6 +320,9 @@ typedef QExplicitlySharedDataPointer<HeightfieldHeight> HeightfieldHeightPointer
class HeightfieldHeight : public HeightfieldData {
public:
static const int HEIGHT_BORDER = 1;
static const int HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER;
HeightfieldHeight(int width, const QVector<quint16>& contents);
HeightfieldHeight(Bitstream& in, int bytes);
HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference);
@ -340,6 +345,36 @@ Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
/// Allows editing heightfield height blocks.
class HeightfieldHeightEditor : public QWidget {
Q_OBJECT
Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged USER true)
public:
HeightfieldHeightEditor(QWidget* parent = NULL);
signals:
void heightChanged(const HeightfieldHeightPointer& height);
public slots:
void setHeight(const HeightfieldHeightPointer& height);
private slots:
void select();
void clear();
private:
HeightfieldHeightPointer _height;
QPushButton* _select;
QPushButton* _clear;
};
typedef QExplicitlySharedDataPointer<HeightfieldColor> HeightfieldColorPointer;
/// A block of color data associated with a heightfield.
@ -368,6 +403,36 @@ Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference);
/// Allows editing heightfield color blocks.
class HeightfieldColorEditor : public QWidget {
Q_OBJECT
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged USER true)
public:
HeightfieldColorEditor(QWidget* parent = NULL);
signals:
void colorChanged(const HeightfieldColorPointer& color);
public slots:
void setColor(const HeightfieldColorPointer& color);
private slots:
void select();
void clear();
private:
HeightfieldColorPointer _color;
QPushButton* _select;
QPushButton* _clear;
};
typedef QExplicitlySharedDataPointer<HeightfieldMaterial> HeightfieldMaterialPointer;
/// A block of material data associated with a heightfield.
@ -405,7 +470,7 @@ class Heightfield : public Transformable {
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged)
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged)
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged DESIGNABLE false)
public: