diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 096798e77e..32ffedf0c8 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -118,6 +118,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new SetSpannerTool(this)); addTool(new ImportHeightfieldTool(this)); addTool(new EraseHeightfieldTool(this)); + addTool(new HeightBrushTool(this)); updateAttributes(); @@ -1080,3 +1081,46 @@ void EraseHeightfieldTool::apply() { message.edit = QVariant::fromValue(edit); Application::getInstance()->getMetavoxels()->applyEdit(message, true); } + +HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) : + MetavoxelTool(editor, name, false) { + + QWidget* widget = new QWidget(); + widget->setLayout(_form = new QFormLayout()); + layout()->addWidget(widget); + + _form->addRow("Radius:", _radius = new QDoubleSpinBox()); + _radius->setSingleStep(0.01); + _radius->setMaximum(FLT_MAX); + _radius->setValue(1.0); +} + +void HeightfieldBrushTool::render() { + // find the intersection with the heightfield + glm::vec3 origin = Application::getInstance()->getMouseRayOrigin(); + glm::vec3 direction = Application::getInstance()->getMouseRayDirection(); + + float distance = -origin.y / direction.y; + glm::vec3 point = origin + distance * direction; + + point.y = Application::getInstance()->getMetavoxels()->getHeight(point); + + glPushMatrix(); + glTranslatef(point.x, point.y, point.z); + + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + + glutSolidSphere(0.1f, 8, 8); + + glPopMatrix(); +} + +HeightBrushTool::HeightBrushTool(MetavoxelEditor* editor) : + HeightfieldBrushTool(editor, "Height Brush") { + + _form->addRow("Height:", _height = new QDoubleSpinBox()); + _height->setMinimum(-FLT_MAX); + _height->setMaximum(FLT_MAX); + _height->setValue(1.0); +} + diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index cc30896c49..50b477a2bf 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -282,7 +282,7 @@ private: HeightfieldPreview _preview; }; -// Allows clearing heighfield blocks. +/// Allows clearing heighfield blocks. class EraseHeightfieldTool : public HeightfieldTool { Q_OBJECT @@ -302,4 +302,33 @@ private: QSpinBox* _length; }; +/// Base class for tools that allow painting on heightfields. +class HeightfieldBrushTool : public MetavoxelTool { + Q_OBJECT + +public: + + HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name); + + virtual void render(); + +protected: + + QFormLayout* _form; + QDoubleSpinBox* _radius; +}; + +/// Allows raising or lowering parts of the heightfield. +class HeightBrushTool : public HeightfieldBrushTool { + Q_OBJECT + +public: + + HeightBrushTool(MetavoxelEditor* editor); + +private: + + QDoubleSpinBox* _height; +}; + #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index a2d3410314..d86ad86df4 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -78,6 +78,79 @@ void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool re QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); } +class HeightVisitor : public MetavoxelVisitor { +public: + + float height; + + HeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); + + virtual int visit(MetavoxelInfo& info); + +private: + + glm::vec3 _location; +}; + +HeightVisitor::HeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), + QVector(), lod), + height(-FLT_MAX), + _location(location) { +} + +static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); +static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f; + +int HeightVisitor::visit(MetavoxelInfo& info) { + glm::vec3 relative = _location - info.minimum; + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || + height >= info.minimum.y + info.size) { + return STOP_RECURSION; + } + if (!info.isLeaf) { + return REVERSE_ORDER; + } + HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue(); + if (!pointer) { + return STOP_RECURSION; + } + const QByteArray& contents = pointer->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int size = glm::sqrt((float)contents.size()); + int highest = size - 1; + relative *= highest / info.size; + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = qMin(qMax((int)floors.x, 0), highest); + int floorZ = qMin(qMax((int)floors.z, 0), highest); + int ceilX = qMin(qMax((int)ceils.x, 0), highest); + int ceilZ = qMin(qMax((int)ceils.z, 0), highest); + float upperLeft = src[floorZ * size + floorX]; + float lowerRight = src[ceilZ * size + ceilX]; + float interpolatedHeight; + if (fracts.x > fracts.z) { + float upperRight = src[floorZ * size + ceilX]; + interpolatedHeight = glm::mix(glm::mix(upperLeft, upperRight, fracts.x), lowerRight, fracts.z); + + } else { + float lowerLeft = src[ceilZ * size + floorX]; + interpolatedHeight = glm::mix(upperLeft, glm::mix(lowerLeft, lowerRight, fracts.x), fracts.z); + } + if (interpolatedHeight == 0.0f) { + return STOP_RECURSION; + } + height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); + return SHORT_CIRCUIT; +} + +float MetavoxelClientManager::getHeight(const glm::vec3& location) { + HeightVisitor visitor(getLOD(), location); + guide(visitor); + return visitor.height; +} + MetavoxelLOD MetavoxelClientManager::getLOD() { return MetavoxelLOD(); } diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 333b709b9e..22c71bd63b 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -43,6 +43,8 @@ public: Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); + Q_INVOKABLE float getHeight(const glm::vec3& location); + /// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread. virtual MetavoxelLOD getLOD();