1110 lines · 39.5 KB · C++
RawDownload
////  MetavoxelEditor.cpp//  interface/src/ui////  Created by Andrzej Kapolka on 1/21/14.//  Copyright 2014 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include <limits> // include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL#include "InterfaceConfig.h" #include <QComboBox>#include <QDialogButtonBox>#include <QDoubleSpinBox>#include <QFileDialog>#include <QFormLayout>#include <QGroupBox>#include <QLineEdit>#include <QListWidget>#include <QMessageBox>#include <QMetaProperty>#include <QOpenGLFramebufferObject>#include <QPushButton>#include <QRunnable>#include <QScrollArea>#include <QSpinBox>#include <QThreadPool>#include <QVBoxLayout> #include <AttributeRegistry.h>#include <MetavoxelMessages.h>#include <MetavoxelUtil.h> #include "Application.h"#include "MetavoxelEditor.h" using namespace std; enum GridPlane {    GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ}; const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); MetavoxelEditor::MetavoxelEditor() :    QWidget(Application::getInstance()->getWindow(), Qt::Tool) {        setWindowTitle("Metavoxel Editor");    setAttribute(Qt::WA_DeleteOnClose);     QVBoxLayout* topLayout = new QVBoxLayout();    setLayout(topLayout);        QGroupBox* attributeGroup = new QGroupBox();    attributeGroup->setTitle("Attributes");    topLayout->addWidget(attributeGroup);        QVBoxLayout* attributeLayout = new QVBoxLayout();    attributeGroup->setLayout(attributeLayout);        attributeLayout->addWidget(_attributes = new QListWidget());    connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(selectedAttributeChanged()));     QHBoxLayout* attributeButtonLayout = new QHBoxLayout();    attributeLayout->addLayout(attributeButtonLayout);     QPushButton* newAttribute = new QPushButton("New...");    attributeButtonLayout->addWidget(newAttribute, 1);    connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute()));     attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"), 1);    _deleteAttribute->setEnabled(false);    connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute()));     attributeButtonLayout->addWidget(_showAll = new QCheckBox("Show All"));    connect(_showAll, SIGNAL(clicked()), SLOT(updateAttributes()));     QFormLayout* formLayout = new QFormLayout();    topLayout->addLayout(formLayout);        formLayout->addRow("Grid Plane:", _gridPlane = new QComboBox());    _gridPlane->addItem("X/Y");    _gridPlane->addItem("X/Z");    _gridPlane->addItem("Y/Z");    _gridPlane->setCurrentIndex(GRID_PLANE_XZ);    connect(_gridPlane, SIGNAL(currentIndexChanged(int)), SLOT(centerGridPosition()));        formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox());    _gridSpacing->setMinimum(-FLT_MAX);    _gridSpacing->setMaximum(FLT_MAX);    _gridSpacing->setPrefix("2^");    _gridSpacing->setValue(-3.0);    connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition()));     formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox());    _gridPosition->setMinimum(-FLT_MAX);    _gridPosition->setMaximum(FLT_MAX);    alignGridPosition();    centerGridPosition();        formLayout->addRow("Tool:", _toolBox = new QComboBox());    connect(_toolBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTool()));        _value = new QGroupBox();    _value->setTitle("Value");    topLayout->addWidget(_value);        QVBoxLayout* valueLayout = new QVBoxLayout();    _value->setLayout(valueLayout);     valueLayout->addWidget(_valueArea = new QScrollArea());    _valueArea->setMinimumHeight(200);    _valueArea->setWidgetResizable(true);     addTool(new BoxSetTool(this));    addTool(new GlobalSetTool(this));    addTool(new InsertSpannerTool(this));    addTool(new RemoveSpannerTool(this));    addTool(new ClearSpannersTool(this));    addTool(new HeightfieldHeightBrushTool(this));    addTool(new HeightfieldMaterialBrushTool(this));    addTool(new ImportHeightfieldTool(this));    addTool(new VoxelMaterialBoxTool(this));    addTool(new VoxelMaterialSpannerTool(this));    addTool(new VoxelMaterialBrushTool(this));    addTool(new VoxelSculptBrushTool(this));        updateAttributes();        connect(Application::getInstance(), SIGNAL(simulating(float)), SLOT(simulate(float)));    connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render()));    connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering,        this, &MetavoxelEditor::renderPreview);        Application::getInstance()->getGLWidget()->installEventFilter(this);        show();        if (_gridProgram.isLinked()) {        return;    }            _gridProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/grid.frag");    _gridProgram.link();} 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 GRID_PLANE_XZ:            return glm::angleAxis(-PI_OVER_TWO, glm::vec3(1.0f, 0.0f, 0.0f));                    case GRID_PLANE_YZ:        default:            return glm::angleAxis(PI_OVER_TWO, glm::vec3(0.0f, 1.0f, 0.0f));    }} 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    MetavoxelTool* tool = getActiveTool();    return tool && tool->eventFilter(watched, event);} void MetavoxelEditor::selectedAttributeChanged() {    _toolBox->clear();        QString selected = getSelectedAttribute();    if (selected.isNull()) {        _deleteAttribute->setEnabled(false);        _toolBox->setEnabled(false);         _value->setVisible(false);        return;    }    _deleteAttribute->setEnabled(true);    _toolBox->setEnabled(true);        AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);    foreach (MetavoxelTool* tool, _tools) {        if (tool->appliesTo(attribute) && (tool->isUserFacing() || _showAll->isChecked())) {            _toolBox->addItem(tool->objectName(), QVariant::fromValue(tool));        }    }    _value->setVisible(true);        if (_valueArea->widget()) {        delete _valueArea->widget();    }    QWidget* editor = attribute->createEditor();    if (editor) {        editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);        _valueArea->setWidget(editor);    }    updateTool();} void MetavoxelEditor::createNewAttribute() {    QDialog dialog(this);    dialog.setWindowTitle("New Attribute");        QVBoxLayout layout;    dialog.setLayout(&layout);        QFormLayout form;    layout.addLayout(&form);        QLineEdit name;    form.addRow("Name:", &name);        SharedObjectEditor editor(&Attribute::staticMetaObject, false);    editor.setObject(new FloatAttribute());    layout.addWidget(&editor);        QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);    dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept()));    dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject()));        layout.addWidget(&buttons);        if (!dialog.exec()) {        return;    }    QString nameText = name.text().trimmed();    SharedObjectPointer attribute = editor.getObject();    attribute->setObjectName(nameText);    AttributeRegistry::getInstance()->registerAttribute(attribute.staticCast<Attribute>());        updateAttributes(nameText);} void MetavoxelEditor::deleteSelectedAttribute() {    AttributeRegistry::getInstance()->deregisterAttribute(getSelectedAttribute());    _attributes->selectionModel()->clear();    updateAttributes();} void MetavoxelEditor::centerGridPosition() {    const float CENTER_OFFSET = 0.625f;    float eyePosition = (glm::inverse(getGridRotation()) * Application::getInstance()->getCamera()->getPosition()).z -        Application::getInstance()->getAvatar()->getScale() * CENTER_OFFSET;    double step = getGridSpacing();    _gridPosition->setValue(step * floor(eyePosition / step));} void MetavoxelEditor::alignGridPosition() {    // make sure our grid position matches our grid spacing    double step = getGridSpacing();    _gridPosition->setSingleStep(step);    _gridPosition->setValue(step * floor(_gridPosition->value() / step));} void MetavoxelEditor::updateAttributes(const QString& select) {    // remember the selection in order to preserve it    QString selected = select.isNull() ? getSelectedAttribute() : select;    _attributes->clear();        // sort the names for consistent ordering    QList<QString> names;    if (_showAll->isChecked()) {        names = AttributeRegistry::getInstance()->getAttributes().keys();        } else {        foreach (const AttributePointer& attribute, AttributeRegistry::getInstance()->getAttributes()) {            if (attribute->isUserFacing()) {                names.append(attribute->getName());            }        }    }    qSort(names);        foreach (const QString& name, names) {        QListWidgetItem* item = new QListWidgetItem(name);        _attributes->addItem(item);        if (name == selected || selected.isNull()) {            item->setSelected(true);            selected = name;        }    }} void MetavoxelEditor::updateTool() {    MetavoxelTool* active = getActiveTool();    foreach (MetavoxelTool* tool, _tools) {        tool->setVisible(tool == active);    }    _value->setVisible(active && active->getUsesValue());} void MetavoxelEditor::simulate(float deltaTime) {    MetavoxelTool* tool = getActiveTool();    if (tool) {        tool->simulate(deltaTime);    }} const float GRID_BRIGHTNESS = 0.5f; void MetavoxelEditor::render() {    glDisable(GL_LIGHTING);        MetavoxelTool* tool = getActiveTool();    if (tool) {        tool->render();    }        glDepthMask(GL_FALSE);        glPushMatrix();        glm::quat rotation = getGridRotation();    glm::vec3 axis = glm::axis(rotation);    glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);        glLineWidth(1.0f);        // center the grid around the camera position on the plane    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), _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);        _gridProgram.release();        glPopMatrix();        glEnable(GL_LIGHTING);    glDepthMask(GL_TRUE);} void MetavoxelEditor::renderPreview() {    MetavoxelTool* tool = getActiveTool();    if (tool) {        tool->renderPreview();    }} void MetavoxelEditor::addTool(MetavoxelTool* tool) {    _tools.append(tool);    layout()->addWidget(tool);} MetavoxelTool* MetavoxelEditor::getActiveTool() const {    int index = _toolBox->currentIndex();    return (index == -1) ? NULL : static_cast<MetavoxelTool*>(_toolBox->itemData(index).value<QObject*>());} ProgramObject MetavoxelEditor::_gridProgram; MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :    _editor(editor),    _usesValue(usesValue),    _userFacing(userFacing) {        QVBoxLayout* layout = new QVBoxLayout();    setLayout(layout);        setObjectName(name);    setVisible(false);} bool MetavoxelTool::appliesTo(const AttributePointer& attribute) const {    // shared object sets are a special case    return !attribute->inherits("SharedObjectSetAttribute");} void MetavoxelTool::simulate(float deltaTime) {    // nothing by default} void MetavoxelTool::render() {    // nothing by default} void MetavoxelTool::renderPreview() {    // nothing by default} BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :    MetavoxelTool(editor, name, usesValue, userFacing) {        resetState();} void BoxTool::render() {    if (Application::getInstance()->isMouseHidden()) {        resetState();        return;    }    QString selected = _editor->getSelectedAttribute();    if (selected.isNull()) {        resetState();        return;    }    glDepthMask(GL_FALSE);        glPushMatrix();        glm::quat rotation = _editor->getGridRotation();    glm::vec3 axis = glm::axis(rotation);    glRotatef(glm::degrees(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 = shouldSnapToGrid() ? _editor->getGridSpacing() : 0.0f;    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 == 0.0f ? projection : 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 == 0.0f) ? _mousePosition : 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();    }        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 = getColor();            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();       }

Lines 1–500 of 1110.