diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake index f0c9ebc6a0..f29c3400bb 100644 --- a/cmake/macros/AutoMTC.cmake +++ b/cmake/macros/AutoMTC.cmake @@ -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() diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 9012248484..0ba0b08633 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -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 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()->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(); - 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 selectedItems = _attributes->selectedItems(); - return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +MetavoxelTool* MetavoxelEditor::getActiveTool() const { + return static_cast(_toolBox->itemData(_toolBox->currentIndex()).value()); } -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(); + 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); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 5e941f3540..be2afd0a36 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -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__) */ diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 989ed6d4a7..491d537b1a 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -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) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index b0e24405e6..6296b93693 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -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) { diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 380df4cac1..9f3ccedc0a 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -9,24 +9,35 @@ #include "MetavoxelData.h" #include "MetavoxelMessages.h" -class EditVisitor : public MetavoxelVisitor { +void MetavoxelEditMessage::apply(MetavoxelData& data) const { + static_cast(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(), QVector() << 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(), QVector() << 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); +} + diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 1c547809fb..165accbbb1 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -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__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 8b33416dc3..f7c2a92b75 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -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 && diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 49039090a0..37a6c8bcda 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -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; }; diff --git a/tests/metavoxels/CMakeLists.txt b/tests/metavoxels/CMakeLists.txt index 416f398470..9d21dd2a44 100644 --- a/tests/metavoxels/CMakeLists.txt +++ b/tests/metavoxels/CMakeLists.txt @@ -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