mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-09 09:02:29 +02:00
Global edits, edits use virtual function.
This commit is contained in:
parent
5b207e4f8e
commit
bb56e2847f
10 changed files with 381 additions and 175 deletions
|
@ -3,20 +3,11 @@ macro(AUTO_MTC TARGET ROOT_DIR)
|
|||
add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc)
|
||||
endif (NOT TARGET mtc)
|
||||
|
||||
set(AUTOMTC_SRC ${TARGET}_automtc.cpp)
|
||||
|
||||
file(GLOB INCLUDE_FILES src/*.h)
|
||||
|
||||
add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp
|
||||
${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES})
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Script REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp)
|
||||
|
||||
qt5_use_modules(${TARGET}_automtc Core Script Widgets)
|
||||
|
||||
target_link_libraries(${TARGET} ${TARGET}_automtc)
|
||||
add_custom_command(OUTPUT ${AUTOMTC_SRC} COMMAND mtc -o ${AUTOMTC_SRC} ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES})
|
||||
|
||||
endmacro()
|
||||
|
||||
|
|
|
@ -81,6 +81,9 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
alignGridPosition();
|
||||
centerGridPosition();
|
||||
|
||||
formLayout->addRow("Tool:", _toolBox = new QComboBox());
|
||||
connect(_toolBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTool()));
|
||||
|
||||
_value = new QGroupBox();
|
||||
_value->setTitle("Value");
|
||||
topLayout->addWidget(_value);
|
||||
|
@ -91,14 +94,20 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
valueLayout->addWidget(_valueArea = new QScrollArea());
|
||||
_valueArea->setWidgetResizable(true);
|
||||
|
||||
BoxSetTool* boxSetTool = new BoxSetTool(this);
|
||||
topLayout->addWidget(boxSetTool);
|
||||
_toolBox->addItem("Set Value (Box)", QVariant::fromValue(boxSetTool));
|
||||
|
||||
GlobalSetTool* globalSetTool = new GlobalSetTool(this);
|
||||
topLayout->addWidget(globalSetTool);
|
||||
_toolBox->addItem("Set Value (Global)", QVariant::fromValue(globalSetTool));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render()));
|
||||
|
||||
Application::getInstance()->getGLWidget()->installEventFilter(this);
|
||||
|
||||
resetState();
|
||||
|
||||
show();
|
||||
|
||||
if (_gridProgram.isLinked()) {
|
||||
|
@ -109,43 +118,42 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
_gridProgram.link();
|
||||
}
|
||||
|
||||
bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) {
|
||||
switch (_state) {
|
||||
case HOVERING_STATE:
|
||||
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
|
||||
_state = DRAGGING_STATE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
QString MetavoxelEditor::getSelectedAttribute() const {
|
||||
QList<QListWidgetItem*> selectedItems = _attributes->selectedItems();
|
||||
return selectedItems.isEmpty() ? QString() : selectedItems.first()->text();
|
||||
}
|
||||
|
||||
double MetavoxelEditor::getGridSpacing() const {
|
||||
return pow(2.0, _gridSpacing->value());
|
||||
}
|
||||
|
||||
double MetavoxelEditor::getGridPosition() const {
|
||||
return _gridPosition->value();
|
||||
}
|
||||
|
||||
glm::quat MetavoxelEditor::getGridRotation() const {
|
||||
// for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there
|
||||
switch (_gridPlane->currentIndex()) {
|
||||
case GRID_PLANE_XY:
|
||||
return glm::quat();
|
||||
|
||||
case DRAGGING_STATE:
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
_state = RAISING_STATE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case GRID_PLANE_XZ:
|
||||
return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f);
|
||||
|
||||
case RAISING_STATE:
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
if (_height != 0) {
|
||||
// find the start and end corners in X/Y
|
||||
float base = _gridPosition->value();
|
||||
float top = base + _height;
|
||||
glm::quat rotation = getGridRotation();
|
||||
glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top));
|
||||
float spacing = getGridSpacing();
|
||||
glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) +
|
||||
glm::vec2(spacing, spacing), glm::max(base, top));
|
||||
|
||||
// find the minimum and maximum extents after rotation
|
||||
applyValue(glm::min(start, end), glm::max(start, end));
|
||||
}
|
||||
resetState();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case GRID_PLANE_YZ:
|
||||
default:
|
||||
return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant MetavoxelEditor::getValue() const {
|
||||
QWidget* editor = _valueArea->widget();
|
||||
return editor ? editor->metaObject()->userProperty().read(editor) : QVariant();
|
||||
}
|
||||
|
||||
bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) {
|
||||
// pass along to the active tool
|
||||
return getActiveTool()->eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void MetavoxelEditor::selectedAttributeChanged() {
|
||||
|
@ -222,13 +230,15 @@ void MetavoxelEditor::alignGridPosition() {
|
|||
_gridPosition->setValue(step * floor(_gridPosition->value() / step));
|
||||
}
|
||||
|
||||
void MetavoxelEditor::render() {
|
||||
QString selected = getSelectedAttribute();
|
||||
if (selected.isNull()) {
|
||||
resetState();
|
||||
return;
|
||||
void MetavoxelEditor::updateTool() {
|
||||
for (int i = 0; i < _toolBox->count(); i++) {
|
||||
_toolBox->itemData(i).value<QWidget*>()->setVisible(i == _toolBox->currentIndex());
|
||||
}
|
||||
}
|
||||
|
||||
const float GRID_BRIGHTNESS = 0.5f;
|
||||
|
||||
void MetavoxelEditor::render() {
|
||||
glDisable(GL_LIGHTING);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
|
@ -238,85 +248,22 @@ void MetavoxelEditor::render() {
|
|||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
|
||||
|
||||
glm::quat inverseRotation = glm::inverse(rotation);
|
||||
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
|
||||
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
|
||||
float spacing = getGridSpacing();
|
||||
float position = _gridPosition->value();
|
||||
if (_state == RAISING_STATE) {
|
||||
// find the plane at the mouse position, orthogonal to the plane, facing the eye position
|
||||
glLineWidth(4.0f);
|
||||
glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition();
|
||||
glm::vec3 mousePoint = glm::vec3(_mousePosition, position);
|
||||
glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint);
|
||||
glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
float divisor = glm::dot(normal, rayDirection);
|
||||
if (fabs(divisor) > EPSILON) {
|
||||
float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor;
|
||||
float projection = rayOrigin.z + distance * rayDirection.z;
|
||||
_height = spacing * roundf(projection / spacing) - position;
|
||||
}
|
||||
} else if (fabs(rayDirection.z) > EPSILON) {
|
||||
// find the intersection of the rotated mouse ray with the plane
|
||||
float distance = (position - rayOrigin.z) / rayDirection.z;
|
||||
_mousePosition = glm::vec2(rayOrigin + rayDirection * distance);
|
||||
glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing);
|
||||
|
||||
if (_state == HOVERING_STATE) {
|
||||
_startPosition = _endPosition = snappedPosition;
|
||||
glLineWidth(2.0f);
|
||||
|
||||
} else if (_state == DRAGGING_STATE) {
|
||||
_endPosition = snappedPosition;
|
||||
glLineWidth(4.0f);
|
||||
}
|
||||
} else {
|
||||
// cancel any operation in progress
|
||||
resetState();
|
||||
}
|
||||
|
||||
const float GRID_BRIGHTNESS = 0.5f;
|
||||
if (_startPosition != INVALID_VECTOR) {
|
||||
glm::vec2 minimum = glm::min(_startPosition, _endPosition);
|
||||
glm::vec2 maximum = glm::max(_startPosition, _endPosition);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(minimum.x, minimum.y, position);
|
||||
glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height);
|
||||
|
||||
glTranslatef(0.5f, 0.5f, 0.5f);
|
||||
if (_state != HOVERING_STATE) {
|
||||
const float BOX_ALPHA = 0.25f;
|
||||
QColor color = getValue().value<QColor>();
|
||||
if (color.isValid()) {
|
||||
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
|
||||
} else {
|
||||
glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA);
|
||||
}
|
||||
glEnable(GL_CULL_FACE);
|
||||
glutSolidCube(1.0);
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
glutWireCube(1.0);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
} else {
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
}
|
||||
getActiveTool()->render();
|
||||
|
||||
glLineWidth(1.0f);
|
||||
|
||||
// center the grid around the camera position on the plane
|
||||
glm::vec3 rotated = inverseRotation * Application::getInstance()->getCamera()->getPosition();
|
||||
glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition();
|
||||
float spacing = getGridSpacing();
|
||||
const int GRID_DIVISIONS = 300;
|
||||
glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position);
|
||||
spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), _gridPosition->value());
|
||||
|
||||
float scale = GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
|
||||
_gridProgram.bind();
|
||||
|
||||
Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
|
||||
|
@ -348,49 +295,173 @@ void MetavoxelEditor::updateAttributes(const QString& select) {
|
|||
}
|
||||
}
|
||||
|
||||
QString MetavoxelEditor::getSelectedAttribute() const {
|
||||
QList<QListWidgetItem*> selectedItems = _attributes->selectedItems();
|
||||
return selectedItems.isEmpty() ? QString() : selectedItems.first()->text();
|
||||
MetavoxelTool* MetavoxelEditor::getActiveTool() const {
|
||||
return static_cast<MetavoxelTool*>(_toolBox->itemData(_toolBox->currentIndex()).value<QObject*>());
|
||||
}
|
||||
|
||||
double MetavoxelEditor::getGridSpacing() const {
|
||||
return pow(2.0, _gridSpacing->value());
|
||||
ProgramObject MetavoxelEditor::_gridProgram;
|
||||
|
||||
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor) :
|
||||
_editor(editor) {
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
setLayout(layout);
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
glm::quat MetavoxelEditor::getGridRotation() const {
|
||||
// for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there
|
||||
switch (_gridPlane->currentIndex()) {
|
||||
case GRID_PLANE_XY:
|
||||
return glm::quat();
|
||||
void MetavoxelTool::render() {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
|
||||
MetavoxelTool(editor) {
|
||||
|
||||
resetState();
|
||||
}
|
||||
|
||||
void BoxSetTool::render() {
|
||||
QString selected = _editor->getSelectedAttribute();
|
||||
if (selected.isNull()) {
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
glm::quat rotation = _editor->getGridRotation();
|
||||
glm::quat inverseRotation = glm::inverse(rotation);
|
||||
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
|
||||
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
|
||||
float spacing = _editor->getGridSpacing();
|
||||
float position = _editor->getGridPosition();
|
||||
if (_state == RAISING_STATE) {
|
||||
// find the plane at the mouse position, orthogonal to the plane, facing the eye position
|
||||
glLineWidth(4.0f);
|
||||
glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition();
|
||||
glm::vec3 mousePoint = glm::vec3(_mousePosition, position);
|
||||
glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint);
|
||||
glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
float divisor = glm::dot(normal, rayDirection);
|
||||
if (fabs(divisor) > EPSILON) {
|
||||
float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor;
|
||||
float projection = rayOrigin.z + distance * rayDirection.z;
|
||||
_height = spacing * roundf(projection / spacing) - position;
|
||||
}
|
||||
} else if (fabs(rayDirection.z) > EPSILON) {
|
||||
// find the intersection of the rotated mouse ray with the plane
|
||||
float distance = (position - rayOrigin.z) / rayDirection.z;
|
||||
_mousePosition = glm::vec2(rayOrigin + rayDirection * distance);
|
||||
glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing);
|
||||
|
||||
if (_state == HOVERING_STATE) {
|
||||
_startPosition = _endPosition = snappedPosition;
|
||||
glLineWidth(2.0f);
|
||||
|
||||
case GRID_PLANE_XZ:
|
||||
return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f);
|
||||
|
||||
case GRID_PLANE_YZ:
|
||||
default:
|
||||
return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f);
|
||||
} else if (_state == DRAGGING_STATE) {
|
||||
_endPosition = snappedPosition;
|
||||
glLineWidth(4.0f);
|
||||
}
|
||||
} else {
|
||||
// cancel any operation in progress
|
||||
resetState();
|
||||
}
|
||||
|
||||
if (_startPosition != INVALID_VECTOR) {
|
||||
glm::vec2 minimum = glm::min(_startPosition, _endPosition);
|
||||
glm::vec2 maximum = glm::max(_startPosition, _endPosition);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(minimum.x, minimum.y, position);
|
||||
glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height);
|
||||
|
||||
glTranslatef(0.5f, 0.5f, 0.5f);
|
||||
if (_state != HOVERING_STATE) {
|
||||
const float BOX_ALPHA = 0.25f;
|
||||
QColor color = _editor->getValue().value<QColor>();
|
||||
if (color.isValid()) {
|
||||
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
|
||||
} else {
|
||||
glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA);
|
||||
}
|
||||
glEnable(GL_CULL_FACE);
|
||||
glutSolidCube(1.0);
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
glutWireCube(1.0);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelEditor::resetState() {
|
||||
bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
|
||||
switch (_state) {
|
||||
case HOVERING_STATE:
|
||||
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
|
||||
_state = DRAGGING_STATE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case DRAGGING_STATE:
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
_state = RAISING_STATE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAISING_STATE:
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
if (_height != 0) {
|
||||
// find the start and end corners in X/Y
|
||||
float base = _editor->getGridPosition();
|
||||
float top = base + _height;
|
||||
glm::quat rotation = _editor->getGridRotation();
|
||||
glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top));
|
||||
float spacing = _editor->getGridSpacing();
|
||||
glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) +
|
||||
glm::vec2(spacing, spacing), glm::max(base, top));
|
||||
|
||||
// find the minimum and maximum extents after rotation
|
||||
applyValue(glm::min(start, end), glm::max(start, end));
|
||||
}
|
||||
resetState();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BoxSetTool::resetState() {
|
||||
_state = HOVERING_STATE;
|
||||
_startPosition = INVALID_VECTOR;
|
||||
_height = 0.0f;
|
||||
}
|
||||
|
||||
void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute());
|
||||
void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
|
||||
if (!attribute) {
|
||||
return;
|
||||
}
|
||||
OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue()));
|
||||
MetavoxelEditMessage edit = { { minimum, maximum }, getGridSpacing(), value };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(edit);
|
||||
OwnedAttributeValue value(attribute, attribute->createFromVariant(_editor->getValue()));
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(BoxSetEdit(Box(minimum, maximum),
|
||||
_editor->getGridSpacing(), value)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message);
|
||||
}
|
||||
|
||||
QVariant MetavoxelEditor::getValue() const {
|
||||
QWidget* editor = _valueArea->widget();
|
||||
return editor ? editor->metaObject()->userProperty().read(editor) : QVariant();
|
||||
GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) :
|
||||
MetavoxelTool(editor) {
|
||||
|
||||
QPushButton* button = new QPushButton("Apply");
|
||||
layout()->addWidget(button);
|
||||
connect(button, SIGNAL(clicked()), SLOT(apply()));
|
||||
}
|
||||
|
||||
ProgramObject MetavoxelEditor::_gridProgram;
|
||||
void GlobalSetTool::apply() {
|
||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
|
||||
if (!attribute) {
|
||||
return;
|
||||
}
|
||||
OwnedAttributeValue value(attribute, attribute->createFromVariant(_editor->getValue()));
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(GlobalSetEdit(value)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ class QListWidget;
|
|||
class QPushButton;
|
||||
class QScrollArea;
|
||||
|
||||
class MetavoxelTool;
|
||||
|
||||
/// Allows editing metavoxels.
|
||||
class MetavoxelEditor : public QDialog {
|
||||
Q_OBJECT
|
||||
|
@ -28,6 +30,14 @@ public:
|
|||
|
||||
MetavoxelEditor();
|
||||
|
||||
QString getSelectedAttribute() const;
|
||||
|
||||
double getGridSpacing() const;
|
||||
double getGridPosition() const;
|
||||
glm::quat getGridRotation() const;
|
||||
|
||||
QVariant getValue() const;
|
||||
|
||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||
|
||||
private slots:
|
||||
|
@ -37,38 +47,84 @@ private slots:
|
|||
void deleteSelectedAttribute();
|
||||
void centerGridPosition();
|
||||
void alignGridPosition();
|
||||
void updateTool();
|
||||
|
||||
void render();
|
||||
|
||||
private:
|
||||
|
||||
void updateAttributes(const QString& select = QString());
|
||||
QString getSelectedAttribute() const;
|
||||
double getGridSpacing() const;
|
||||
glm::quat getGridRotation() const;
|
||||
void resetState();
|
||||
void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
|
||||
QVariant getValue() const;
|
||||
void updateAttributes(const QString& select = QString());
|
||||
MetavoxelTool* getActiveTool() const;
|
||||
|
||||
QListWidget* _attributes;
|
||||
QPushButton* _deleteAttribute;
|
||||
|
||||
QComboBox* _gridPlane;
|
||||
QDoubleSpinBox* _gridSpacing;
|
||||
QDoubleSpinBox* _gridPosition;
|
||||
|
||||
QComboBox* _toolBox;
|
||||
|
||||
QGroupBox* _value;
|
||||
QScrollArea* _valueArea;
|
||||
|
||||
static ProgramObject _gridProgram;
|
||||
};
|
||||
|
||||
/// Base class for editor tools.
|
||||
class MetavoxelTool : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MetavoxelTool(MetavoxelEditor* editor);
|
||||
|
||||
/// Renders the tool's interface, if any.
|
||||
virtual void render();
|
||||
|
||||
protected:
|
||||
|
||||
MetavoxelEditor* _editor;
|
||||
};
|
||||
|
||||
/// Allows setting the value of a region by dragging out a box.
|
||||
class BoxSetTool : public MetavoxelTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
BoxSetTool(MetavoxelEditor* editor);
|
||||
|
||||
virtual void render();
|
||||
|
||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||
|
||||
private:
|
||||
|
||||
void resetState();
|
||||
void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
|
||||
|
||||
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
|
||||
|
||||
State _state;
|
||||
|
||||
glm::vec2 _mousePosition; ///< the position of the mouse in rotated space
|
||||
|
||||
glm::vec2 _startPosition; ///< the first corner of the selection base
|
||||
glm::vec2 _endPosition; ///< the second corner of the selection base
|
||||
float _height; ///< the selection height
|
||||
};
|
||||
|
||||
/// Allows setting the value across the entire space.
|
||||
class GlobalSetTool : public MetavoxelTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static ProgramObject _gridProgram;
|
||||
GlobalSetTool(MetavoxelEditor* editor);
|
||||
|
||||
private slots:
|
||||
|
||||
void apply();
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__MetavoxelEditor__) */
|
||||
|
|
|
@ -11,12 +11,12 @@ set(TARGET_NAME metavoxels)
|
|||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
||||
include(${MACRO_DIR}/AutoMTC.cmake)
|
||||
auto_mtc(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME} ${AUTOMTC_SRC})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network Script Widgets)
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
|
|
|
@ -43,8 +43,7 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) {
|
|||
|
||||
Box MetavoxelData::getBounds() const {
|
||||
float halfSize = _size * 0.5f;
|
||||
Box bounds = { glm::vec3(-halfSize, -halfSize, -halfSize), glm::vec3(halfSize, halfSize, halfSize) };
|
||||
return bounds;
|
||||
return Box(glm::vec3(-halfSize, -halfSize, -halfSize), glm::vec3(halfSize, halfSize, halfSize));
|
||||
}
|
||||
|
||||
void MetavoxelData::guide(MetavoxelVisitor& visitor) {
|
||||
|
|
|
@ -9,24 +9,35 @@
|
|||
#include "MetavoxelData.h"
|
||||
#include "MetavoxelMessages.h"
|
||||
|
||||
class EditVisitor : public MetavoxelVisitor {
|
||||
void MetavoxelEditMessage::apply(MetavoxelData& data) const {
|
||||
static_cast<const MetavoxelEdit*>(edit.data())->apply(data);
|
||||
}
|
||||
|
||||
MetavoxelEdit::~MetavoxelEdit() {
|
||||
}
|
||||
|
||||
BoxSetEdit::BoxSetEdit(const Box& region, float granularity, const OwnedAttributeValue& value) :
|
||||
region(region), granularity(granularity), value(value) {
|
||||
}
|
||||
|
||||
class BoxSetEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
EditVisitor(const MetavoxelEditMessage& edit);
|
||||
BoxSetEditVisitor(const BoxSetEdit& edit);
|
||||
|
||||
virtual bool visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
const MetavoxelEditMessage& _edit;
|
||||
const BoxSetEdit& _edit;
|
||||
};
|
||||
|
||||
EditVisitor::EditVisitor(const MetavoxelEditMessage& edit) :
|
||||
BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute()),
|
||||
_edit(edit) {
|
||||
}
|
||||
|
||||
bool EditVisitor::visit(MetavoxelInfo& info) {
|
||||
bool BoxSetEditVisitor::visit(MetavoxelInfo& info) {
|
||||
// find the intersection between volume and voxel
|
||||
glm::vec3 minimum = glm::max(info.minimum, _edit.region.minimum);
|
||||
glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.region.maximum);
|
||||
|
@ -48,12 +59,44 @@ bool EditVisitor::visit(MetavoxelInfo& info) {
|
|||
return true; // subdivide
|
||||
}
|
||||
|
||||
void MetavoxelEditMessage::apply(MetavoxelData& data) const {
|
||||
void BoxSetEdit::apply(MetavoxelData& data) const {
|
||||
// expand to fit the entire edit
|
||||
while (!data.getBounds().contains(region)) {
|
||||
data.expand();
|
||||
}
|
||||
|
||||
EditVisitor visitor(*this);
|
||||
BoxSetEditVisitor visitor(*this);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) :
|
||||
value(value) {
|
||||
}
|
||||
|
||||
class GlobalSetEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
GlobalSetEditVisitor(const GlobalSetEdit& edit);
|
||||
|
||||
virtual bool visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
const GlobalSetEdit& _edit;
|
||||
};
|
||||
|
||||
GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute()),
|
||||
_edit(edit) {
|
||||
}
|
||||
|
||||
bool GlobalSetEditVisitor::visit(MetavoxelInfo& info) {
|
||||
info.outputValues[0] = _edit.value;
|
||||
return false; // entirely contained
|
||||
}
|
||||
|
||||
void GlobalSetEdit::apply(MetavoxelData& data) const {
|
||||
GlobalSetEditVisitor visitor(*this);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,13 +67,53 @@ class MetavoxelEditMessage {
|
|||
|
||||
public:
|
||||
|
||||
STREAM Box region;
|
||||
STREAM float granularity;
|
||||
STREAM OwnedAttributeValue value;
|
||||
STREAM QVariant edit;
|
||||
|
||||
void apply(MetavoxelData& data) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage)
|
||||
|
||||
/// Abstract base class for edits.
|
||||
class MetavoxelEdit {
|
||||
public:
|
||||
|
||||
virtual ~MetavoxelEdit();
|
||||
|
||||
virtual void apply(MetavoxelData& data) const = 0;
|
||||
};
|
||||
|
||||
/// An edit that sets the region within a box to a value.
|
||||
class BoxSetEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM Box region;
|
||||
STREAM float granularity;
|
||||
STREAM OwnedAttributeValue value;
|
||||
|
||||
BoxSetEdit(const Box& region = Box(), float granularity = 0.0f,
|
||||
const OwnedAttributeValue& value = OwnedAttributeValue());
|
||||
|
||||
virtual void apply(MetavoxelData& data) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(BoxSetEdit)
|
||||
|
||||
/// An edit that sets the entire tree to a value.
|
||||
class GlobalSetEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM OwnedAttributeValue value;
|
||||
|
||||
GlobalSetEdit(const OwnedAttributeValue& value = OwnedAttributeValue());
|
||||
|
||||
virtual void apply(MetavoxelData& data) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(GlobalSetEdit)
|
||||
|
||||
#endif /* defined(__interface__MetavoxelMessages__) */
|
||||
|
|
|
@ -155,6 +155,10 @@ QByteArray signal(const char* signature) {
|
|||
return signal.replace("dummyMethod()", signature);
|
||||
}
|
||||
|
||||
Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) :
|
||||
minimum(minimum), maximum(maximum) {
|
||||
}
|
||||
|
||||
bool Box::contains(const Box& other) const {
|
||||
return other.minimum.x >= minimum.x && other.maximum.x <= maximum.x &&
|
||||
other.minimum.y >= minimum.y && other.maximum.y <= maximum.y &&
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
STREAM glm::vec3 minimum;
|
||||
STREAM glm::vec3 maximum;
|
||||
|
||||
Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3());
|
||||
|
||||
bool contains(const Box& other) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -12,12 +12,12 @@ find_package(Qt5Network REQUIRED)
|
|||
find_package(Qt5Script REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
include(${MACRO_DIR}/AutoMTC.cmake)
|
||||
auto_mtc(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE ${AUTOMTC_SRC})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network Script Widgets)
|
||||
|
||||
#include glm
|
||||
|
|
Loading…
Reference in a new issue