mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 21:43:13 +02:00
More work on heightfield spanners.
This commit is contained in:
parent
0664ff3181
commit
b94dab0e8f
4 changed files with 263 additions and 24 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
Loading…
Reference in a new issue