Top.Mail.Ru

Qt и QML. mtPaint3D. Создаем утилиту для 3D-рисования. День 5.

Пятый, полуюбилейный день марафона 😉 Что имеется на данный момент:

mtPaint3D.

Не вижу смысла отходить от первоначального плана, который заключался в том, чтобы сначала реализовать полный функционал приложения для конкретного 3D-объекта, а затем без лишних напрягов аналогичным образом расширить уже работающую механику на новые фигуры. Поэтому продолжаем работать с добавляемыми на сцену кубоидами. Панель перемещений/вращений уже позади, на очереди инструменты для изменения уникальных свойств объекта:

Дизайн интерфейса приложения.

В случае кубоида таких свойства три:

  • длина
  • ширина
  • высота

Естественно, сразу надо продумать архитектуру таким образом, чтобы она не требовала никаких изменений при работе с другими фигурами, а использовала точно такой же интерфейс. Поэтому панели свойств объекта я решил придать следующий вид (дизайн и оформление пока не трогаем, дефолтный вид):

Редактор свойств фигуры.

То есть для каждого отдельного свойства фигуры создается слайдер, сопровождающийся названием этого свойства. И так же как и для TransformPanel нужны будут минимальное/максимальное, а также текущее значение для каждого из слайдеров. Код панели, таким образом, вид приобрел следующий, файл CommonObjectPanel.qml:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQml.Models 2.15
import QtQuick.Layouts 1.15
import QtQuick.Shapes 1.15



Rectangle {
    id: rootRectangle
    color: "#a0a080"
    height: controlsColumn.height + 30

    property var parameters: [0, 0, 0]
    property var controlsModel

    Column {
        id: controlsColumn
        spacing: 2
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        Repeater {
            id: controlsRepeater
            model: controlsModel

            Column {
                Label {
                    width: parent.width
                    text: modelData['text']
                    color: "white"
                    horizontalAlignment: Text.AlignHCenter
                    font.pointSize: 10
                }

                Slider {
                    id: slider
                    property var sliderIndex: index

                    from: modelData['sliderMin']
                    value: modelData['sliderCurrent']
                    to: modelData['sliderMax']

                    onVisibleChanged: {
                        if (visible === true) {
                            parameters[sliderIndex] = value;
                        }
                    }

                    onMoved: {
                        parameters[sliderIndex] = value;
                        parametersChanged();
                    }
                }
            }
        }
    }
}

Следующим неизменным шагом добавляем панель в окно приложения сразу после TransformPanel. Но здесь я поместил CommonObjectPanel в отдельный Item, чтобы в будущем оптимизировать работу с панелями, которые будут регулировать свойства объектов:

Item {
	id: objectsPanels
	anchors.left: parent.left
	anchors.right: parent.right
	anchors.top: transformPanel.bottom

	CommonObjectPanel {
		id: commonObjectPanel
		anchors.left: parent.left
		anchors.right: parent.right
		visible: false

		onParametersChanged: {
			if (paintEntity.activePaintObject !== null) {
				paintEntity.activePaintObject.update(parameters);
			}
		}
	}
}

В целом, механизм аналогичен использованному нами ранее. По обновлению параметров, которые хранятся в свойстве parameters в CommonObjectsPanel будет сгенерирован сигнал, которой мы отлавливаем в обработчике onParametersChanged. После чего полученные данные передаем в метод update() класса PaintObject. Пока туда не лезем, закончим с QML. Здесь осталось при изменении активного объекта произвести настройку панели параметров под этот вышеобозначенный объект и, соответственно, отобразить ее:

PaintEntity {
	id: paintEntity

	function hideObjectControls() {
		for (var i = 0; i < objectsPanels.visibleChildren.length; i++) {
			objectsPanels.visibleChildren[i].visible = false;
		}

		transformPanel.visible = false;
		commonObjectPanel.visible = false;
	}

	onActivePaintObjectChanged: {
		hideObjectControls();

		if (activePaintObject !== null) {
			transformPanel.initialPosition = activePaintObject.position;
			transformPanel.reset();
			transformPanel.visible = true;
		}

		switch(activePaintObject.type) {
			case PaintObject.Type.Cuboid:
				commonObjectPanel.controlsModel = [
						{text: qsTr("X extent"), sliderMin: 0.1, sliderMax: baseCuboid.xExtent, sliderCurrent: activePaintObject.parameters[0]},
						{text: qsTr("Y extent"), sliderMin: 0.1, sliderMax: baseCuboid.yExtent, sliderCurrent: activePaintObject.parameters[1]},
						{text: qsTr("Z extent"), sliderMin: 0.1, sliderMax: 10, sliderCurrent: activePaintObject.parameters[2]}
					];

				commonObjectPanel.visible = true;
				break;

			default:
				break;
		}
	}
}

Настройка панели инкапсулирована в одном единственном месте - в изменении модели commonObjectPanel.controlsModel. Каждая строка представляет из себя свойство отдельно взятого слайдера - имя параметра (text), минимально допустимое значение (sliderMin), максимально допустимое значение (sliderMax) и текущее значение (sliderCurrent). Из чего следует, что помимо метода update() для изменения свойств фигуры нам понадобится свойство parameters, из которого как минимум мы будем получать текущие значения. Именно к этому и переходим:

class PaintObject : public QObject
{
    Q_OBJECT
public:
    enum class Type {
        None = 0,
        Cuboid,
        Sphere,
        Cone,
        Cylinder,
        Torus,
        ExtrudedText,
        Curve
    };
    Q_ENUM(Type)

    PaintObject(Qt3DCore::QEntity *parentEntity, quint32 objectId);

    Q_PROPERTY(Type type READ getType WRITE setType);
    Q_PROPERTY(QVector3D position READ getPosition);
    Q_PROPERTY(QVector<qreal> parameters READ getParameters);

    Type getType();
    void setType(Type objectType);
    quint32 getId();
    void setId(quint32 objectId);
    QVector3D getPosition();
    void setPickerEnabled(bool state);
    void startSelectAnimation();
    void startDeleteAnimation();

    Q_INVOKABLE virtual void rotate(const QVector3D &angle);
    Q_INVOKABLE virtual void move(const QVector3D &position);
    Q_INVOKABLE virtual void update(const QVector<qreal> &parameters) = 0;
    virtual QVector<qreal> getParameters() = 0;
// ...............

С технической точки зрения оба метода (void update(const QVector<qreal> &parameters) и QVector<qreal> getParameters()) чисто виртуальные, так как любой отдельно взятый 3D-объект будет иметь свои собственные уникальные свойства.

У нас в наличии пока один лишь кубоид, пишем реализацию для него:

class Cuboid : public PaintObject
{
    Q_OBJECT
public:
    Cuboid(Qt3DCore::QEntity *parentEntity, quint32 objectId, const QVector3D &coordinate = QVector3D(0, 0, 0));
    void onMouseMoved(const QVector3D &coordinate);
    void update(const QVector<qreal> &parameters);
    QVector<qreal> getParameters();

};
Cuboid::Cuboid(Qt3DCore::QEntity *parentEntity, quint32 objectId, const QVector3D &coordinate) :
    PaintObject(parentEntity, objectId)
{
    type = PaintObject::Type::Cuboid;

    Qt3DExtras::QCuboidMesh *mesh = new Qt3DExtras::QCuboidMesh();
    Qt3DCore::QTransform *transform = new Qt3DCore::QTransform();

    mesh->setXExtent(0);
    mesh->setYExtent(0);
    mesh->setZExtent(0);

    transform->setTranslation(QVector3D(coordinate.x(), coordinate.y(), constants::basePlaneZExtent / 2));
    initialCoordinate = coordinate;

    entity->addComponent(mesh);
    entity->addComponent(transform);

    addMaterial();
}



void Cuboid::onMouseMoved(const QVector3D &coordinate)
{
    Qt3DExtras::QCuboidMesh *currentMesh = entity->componentsOfType<Qt3DExtras::QCuboidMesh>().at(0);

    currentMesh->setXExtent(qAbs(coordinate.x() - initialCoordinate.x()));
    currentMesh->setYExtent(qAbs(coordinate.y() - initialCoordinate.y()));
    currentMesh->setZExtent(defaultZExtent);

    Qt3DCore::QTransform *currentTransform = entity->componentsOfType<Qt3DCore::QTransform>().at(0);
    currentTransform->setTranslation(QVector3D((coordinate.x() + initialCoordinate.x()) / 2,
                                               (coordinate.y() + initialCoordinate.y()) / 2,
                                               constants::basePlaneZExtent + defaultZExtent / 2));
}



QVector<qreal> Cuboid::getParameters()
{
    QVector<qreal> parameters;

    for (int i = 0; i < parametersVectorSize; i++)
    {
        parameters.append(0);
    }

    Qt3DExtras::QCuboidMesh *currentMesh = entity->componentsOfType<Qt3DExtras::QCuboidMesh>().at(0);

    parameters[0] = currentMesh->xExtent();
    parameters[1] = currentMesh->yExtent();
    parameters[2] = currentMesh->zExtent();

    return parameters;
}



void Cuboid::update(const QVector<qreal> &parameters)
{
    if (parameters.size() >= parametersVectorSize)
    {
        Qt3DExtras::QCuboidMesh *currentMesh = entity->componentsOfType<Qt3DExtras::QCuboidMesh>().at(0);

        currentMesh->setXExtent(parameters.at(0));
        currentMesh->setYExtent(parameters.at(1));
        currentMesh->setZExtent(parameters.at(2));
    }
}

Ну и проверим, что еще остается )

Прошло как по маслу, поэтому сейчас заодно добавим последнюю из оставшихся интерфейсных частей - панель для выбора цвета объекта:

Панель выбора цвета.

По большому счету, здесь весь интерфейс заключается в одном ComboBox'е, у которого в качестве вариантов для выбора - разные цвета. Я пока накидал всего пару-тройку вариантов, остальные добавим позже в процессе дизайнерско-оформительных процессов. Результат следующий:

QML ComboBox colors.

ColorPanel.qml:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQml.Models 2.15



Rectangle {
    id: rootRectangle
    color: "#a0a080"
    height: colorsComboBox.height + 0
    property var selectedColor: colorsComboBox.model[colorsComboBox.currentIndex]

    function setCurrentItem(itemText) {
        var index = colorsComboBox.find(itemText);

        if (index !== -1) {
            colorsComboBox.currentIndex = index;
        }
    }

    ComboBox {
        id: colorsComboBox
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.verticalCenter: parent.verticalCenter

        model: ["#b22222", "#3cb371", "#4682b4"]

        delegate: ItemDelegate {
            width: colorsComboBox.width
            height: 35

            background: Rectangle {
                color: colorsComboBox.highlightedIndex === index ? "#a0a0a0" : "#a0a080"
            }

            contentItem: Rectangle {
                anchors.fill: parent
                anchors.topMargin: 1
                anchors.bottomMargin: 1
                color: modelData
            }

            highlighted: colorsComboBox.highlightedIndex === index
        }

        contentItem: Rectangle {
            anchors.fill: parent
            color: colorsComboBox.model[colorsComboBox.currentIndex]
        }
    }
}

В main.qml ColorPanel интегрируется плюс-минус точно также как и остальные, поэтому код не привожу. Все будет в конце статьи - полный код + готовый проект для текущего этапа. Кстати, ColorPanel, как и ToolPanel, будет отображаться всегда, независимо от того - выбран ли какой-то объект или нет. Если не выбран, то панель будет задавать цвет для создаваемых объектов. Если же объект активен, то панель будет изменять его текущий цвет.

Для обработки всех упомянутых цветовых махинаций первым делом добавляем к PaintEntity:

Q_INVOKABLE void setActiveColor(const QString &color);

Таким образом QML забрасывает в PaintEntity результаты пользовательских действий, связанных с выбором или изменением цвета. В PaintEntity, как и обсудили, у нас два случая, когда это становится актуальным. Для вновь созданного 3D-объекта:

void PaintEntity::onBasePlaneMousePressed(const QVector3D &coordinate)
{
    switch (activeTool)
    {
        case PaintEntity::ToolType::Cuboid:
            paintObjects.append(QSharedPointer<PaintObject>(new Cuboid(this, paintObjects.size(), coordinate)));
            break;

        default:
            // This branch should never be executed
            break;
    }

    int currentIndex = paintObjects.size() - 1;
    paintObjects.at(currentIndex)->setColor(activeColor);
    QObject::connect(paintObjects.at(currentIndex).data(), &PaintObject::picked,
                     this, &PaintEntity::paintObjectPicked);
    QObject::connect(paintObjects.at(currentIndex).data(), &PaintObject::readyForDeletion,
                     this, &PaintEntity::removePaintObject);
}

И для активного:

void PaintEntity::setActiveColor(const QString &color)
{
    activeColor = color;

    if (activeTool == PaintEntity::ToolType::Select)
    {
        if (activePaintObject != nullptr)
        {
            activePaintObject->setColor(activeColor);
        }
    }
}

И, наконец, результат сегодняшних мыслительных процессов и полный код:

В исходниках по слову color можно локализовать актуальные изменения, не обозначенные в тексте отдельно 👌

#include <QDebug>
#include "paintentity.h"
#include "objects/cuboid.h"



PaintEntity::PaintEntity(QNode *parent) : Qt3DCore::QEntity(parent),
    activeTool(ToolType::Cuboid),
    activePaintObject(nullptr),
    activeColor("#b22222")
{
}



void PaintEntity::paintObjectPicked(quint32 pickedId)
{
    if (activeTool == PaintEntity::ToolType::Select)
    {
        paintObjects.at(pickedId)->startSelectAnimation();
        activePaintObject = paintObjects.at(pickedId).data();
        emit activePaintObjectChanged();
    }
    else
    {
        if (activeTool == PaintEntity::ToolType::Delete)
        {
            paintObjects.at(pickedId)->startDeleteAnimation();
        }
    }
}



void PaintEntity::removePaintObject(quint32 objectId)
{
    if (activePaintObject != nullptr)
    {
        if (objectId == activePaintObject->getId())
        {
            activePaintObject = nullptr;
            emit activePaintObjectChanged();
        }
    }

    paintObjects.remove(objectId);
    updatePaintObjectIds(objectId);
}



void PaintEntity::updatePaintObjectIds(int deletedIndex)
{
    for (int i = deletedIndex; i < paintObjects.size(); i++)
    {
        paintObjects.at(i)->setId(i);
    }
}



void PaintEntity::onBasePlaneMousePressed(const QVector3D &coordinate)
{
    switch (activeTool)
    {
        case PaintEntity::ToolType::Cuboid:
            paintObjects.append(QSharedPointer<PaintObject>(new Cuboid(this, paintObjects.size(), coordinate)));
            break;

        default:
            // This branch should never be executed
            break;
    }

    int currentIndex = paintObjects.size() - 1;
    paintObjects.at(currentIndex)->setColor(activeColor);
    QObject::connect(paintObjects.at(currentIndex).data(), &PaintObject::picked,
                     this, &PaintEntity::paintObjectPicked);
    QObject::connect(paintObjects.at(currentIndex).data(), &PaintObject::readyForDeletion,
                     this, &PaintEntity::removePaintObject);
}



void PaintEntity::onBasePlaneMouseMoved(const QVector3D &coordinate)
{
    paintObjects.at(paintObjects.size() - 1)->onMouseMoved(coordinate);
}



void PaintEntity::setActiveColor(const QString &color)
{
    activeColor = color;

    if (activeTool == PaintEntity::ToolType::Select)
    {
        if (activePaintObject != nullptr)
        {
            activePaintObject->setColor(activeColor);
        }
    }
}



PaintEntity::ToolType PaintEntity::getActiveTool()
{
    return activeTool;
}



void PaintEntity::setActiveTool(ToolType tool)
{
    activeTool = tool;
    bool pickerState = false;

    if ((activeTool == PaintEntity::ToolType::Select) ||
        (activeTool == PaintEntity::ToolType::Delete))
    {
        pickerState = true;
    }

    for (int i = 0; i < paintObjects.size(); i++)
    {
        paintObjects.at(i)->setPickerEnabled(pickerState);
    }
}



PaintObject* PaintEntity::getPaintObject()
{
    return activePaintObject;
}
#ifndef PAINTENTITY_H
#define PAINTENTITY_H



#include <Qt3DCore>
#include <Qt3DExtras>

#include "objects/paintobject.h"



class PaintEntity : public Qt3DCore::QEntity
{
    Q_OBJECT
public:
    enum class ToolType {
        Select = 0,
        Delete,
        Cuboid
    };
    Q_ENUM(ToolType)

    PaintEntity(QNode *parent = nullptr);
    Q_PROPERTY(ToolType activeTool READ getActiveTool WRITE setActiveTool);
    Q_PROPERTY(PaintObject *activePaintObject READ getPaintObject NOTIFY activePaintObjectChanged);

    Q_INVOKABLE void onBasePlaneMousePressed(const QVector3D &coordinate);
    Q_INVOKABLE void onBasePlaneMouseMoved(const QVector3D &coordinate);
    Q_INVOKABLE void setActiveColor(const QString &color);
    PaintObject* getPaintObject();
    ToolType getActiveTool();
    void setActiveTool(ToolType tool);

private slots:
    void paintObjectPicked(quint32 pickedId);
    void removePaintObject(quint32 objectId);

signals:
    void activePaintObjectChanged();

private:
    QVector< QSharedPointer<PaintObject> > paintObjects;
    ToolType activeTool;
    PaintObject *activePaintObject;
    QString activeColor;

    void updatePaintObjectIds(int deletedIndex);
};



#endif // PAINTENTITY_H
#include <QDebug>
#include "paintobject.h"



PaintObject::PaintObject(Qt3DCore::QEntity *parentEntity, quint32 objectId) :
    type(PaintObject::Type::None),
    id(objectId)
{
    createAnimation();

    entity = QSharedPointer<Qt3DCore::QEntity>(new Qt3DCore::QEntity(parentEntity));
    picker = new Qt3DRender::QObjectPicker();
    picker->setEnabled(false);
    picker->setHoverEnabled(true);

    entity->addComponent(picker);

    QObject::connect(picker, &Qt3DRender::QObjectPicker::clicked, this, &PaintObject::onClicked);
}



void PaintObject::createAnimation()
{
    transformAnimationGroup = new QSequentialAnimationGroup(this);

    increaseAnimation = new QPropertyAnimation(this);
    increaseAnimation->setDuration(125);

    decreaseAnimation = new QPropertyAnimation(this);
    decreaseAnimation->setDuration(125);

    deleteAnimation = new QPropertyAnimation(this);
    deleteAnimation->setDuration(50);
    deleteAnimation->setEndValue(QVector3D(0, 0, 0));

    QObject::connect(deleteAnimation, &QPropertyAnimation::finished,
                     this, &PaintObject::deleteAnimationFinished);

    transformAnimationGroup->addAnimation(increaseAnimation);
    transformAnimationGroup->addAnimation(decreaseAnimation);
}



void PaintObject::startSelectAnimation()
{
    QVector3D currentScale3D = getTransform()->scale3D();

    if (transformAnimationGroup->state() == QAbstractAnimation::Stopped)
    {
        increaseAnimation->setTargetObject(getTransform());
        increaseAnimation->setPropertyName("scale3D");
        increaseAnimation->setStartValue(currentScale3D);
        increaseAnimation->setEndValue(currentScale3D * 1.15);

        decreaseAnimation->setTargetObject(getTransform());
        decreaseAnimation->setPropertyName("scale3D");
        decreaseAnimation->setStartValue(currentScale3D * 1.15);
        decreaseAnimation->setEndValue(currentScale3D);

        transformAnimationGroup->start();
    }
}



void PaintObject::startDeleteAnimation()
{
    setPickerEnabled(false);
    QVector3D currentScale3D = getTransform()->scale3D();

    deleteAnimation->setTargetObject(getTransform());
    deleteAnimation->setPropertyName("scale3D");
    deleteAnimation->setStartValue(currentScale3D);

    deleteAnimation->start();
}



void PaintObject::deleteAnimationFinished()
{
    emit readyForDeletion(id);
}



Qt3DCore::QTransform* PaintObject::getTransform()
{
    Qt3DCore::QTransform *transform = entity->componentsOfType<Qt3DCore::QTransform>().at(0);
    return transform;
}



void PaintObject::onClicked(Qt3DRender::QPickEvent *event)
{
    if (event->button() == Qt3DRender::QPickEvent::LeftButton)
    {
        emit picked(id);
    }
}



void PaintObject::addMaterial()
{
    Qt3DExtras::QDiffuseSpecularMaterial *defaultMaterial = new Qt3DExtras::QDiffuseSpecularMaterial();
    defaultMaterial->setAmbient(QColor(Qt::black));
    defaultMaterial->setSpecular(QColor(Qt::white));
    defaultMaterial->setShininess(4);
    entity->addComponent(defaultMaterial);
}



quint32 PaintObject::getId()
{
    return id;
}



void PaintObject::setId(quint32 objectId)
{
    id = objectId;
}



PaintObject::Type PaintObject::getType()
{
    return type;
}



void PaintObject::setType(Type objectType)
{
    type = objectType;
}



void PaintObject::setPickerEnabled(bool state)
{
    picker->setEnabled(state);
}



QVector3D PaintObject::getPosition()
{
    Qt3DCore::QTransform *transform = getTransform();
    return transform->translation();
}



QString PaintObject::getColor()
{
    QString color;

    Qt3DExtras::QDiffuseSpecularMaterial *currentMaterial = getMaterial();
    color = currentMaterial->ambient().name();

    return color;
}



void PaintObject::setColor(const QString &color)
{
    Qt3DExtras::QDiffuseSpecularMaterial *currentMaterial = getMaterial();
    currentMaterial->setAmbient(QColor(color));
}



Qt3DExtras::QDiffuseSpecularMaterial* PaintObject::getMaterial()
{
    Qt3DExtras::QDiffuseSpecularMaterial *material = entity->componentsOfType<Qt3DExtras::QDiffuseSpecularMaterial>().at(0);
    return material;
}



void PaintObject::move(const QVector3D &position)
{
    Qt3DCore::QTransform *transform = getTransform();
    transform->setTranslation(position);
}



void PaintObject::rotate(const QVector3D &angle)
{
    Qt3DCore::QTransform *transform = getTransform();

    transform->setRotationX(transform->rotationX() + angle.x());
    transform->setRotationY(transform->rotationY() + angle.y());
    transform->setRotationZ(transform->rotationZ() + angle.z());
}
#ifndef PAINTOBJECT_H
#define PAINTOBJECT_H



#include <QObject>
#include <Qt3DCore>
#include <Qt3DExtras>
#include <QSharedPointer>
#include "constants.h"



class PaintObject : public QObject
{
    Q_OBJECT
public:
    enum class Type {
        None = 0,
        Cuboid,
        Sphere,
        Cone,
        Cylinder,
        Torus,
        ExtrudedText,
        Curve
    };
    Q_ENUM(Type)

    PaintObject(Qt3DCore::QEntity *parentEntity, quint32 objectId);

    Q_PROPERTY(Type type READ getType WRITE setType);
    Q_PROPERTY(QVector3D position READ getPosition);
    Q_PROPERTY(QVector<qreal> parameters READ getParameters);
    Q_PROPERTY(QString color READ getColor);

    Type getType();
    void setType(Type objectType);
    quint32 getId();
    void setId(quint32 objectId);
    QVector3D getPosition();
    void setPickerEnabled(bool state);
    void startSelectAnimation();
    void startDeleteAnimation();

    Q_INVOKABLE virtual void rotate(const QVector3D &angle);
    Q_INVOKABLE virtual void move(const QVector3D &position);
    Q_INVOKABLE virtual void update(const QVector<qreal> &parameters) = 0;
    virtual QString getColor();
    virtual void setColor(const QString &color);
    virtual QVector<qreal> getParameters() = 0;
    virtual void onMouseMoved(const QVector3D &coordinate) = 0;
    virtual void addMaterial();
    virtual Qt3DCore::QTransform* getTransform();

signals:
    void picked(quint32 pickedId);
    void readyForDeletion(quint32 objectId);

private slots:
    virtual void onClicked(Qt3DRender::QPickEvent *event);
    void deleteAnimationFinished();

protected:
    Type type;
    QSharedPointer<Qt3DCore::QEntity> entity;
    QVector3D initialCoordinate;
    quint32 id;

private:
    Qt3DRender::QObjectPicker *picker;

    QSequentialAnimationGroup *transformAnimationGroup;
    QPropertyAnimation *increaseAnimation;
    QPropertyAnimation *decreaseAnimation;
    QPropertyAnimation *deleteAnimation;

    void createAnimation();
    Qt3DExtras::QDiffuseSpecularMaterial* getMaterial();

};



#endif // PAINTOBJECT_H
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt3D.Core 2.15
import QtQuick3D 1.15
import QtQuick.Scene3D 2.15
import Qt3D.Render 2.15
import Qt3D.Input 2.15
import Qt3D.Extras 2.15
import QtQuick3D.Materials 1.15
import "../qml/objects"

import microtechnics.paintEntity 1.0
import microtechnics.paintObject 1.0



ApplicationWindow {
    id: window
    width: Screen.width * 3 / 4
    height: Screen.height * 3 / 4
    visible: true
    title: qsTr("mtPaint3D")
    color: "#505050"

    onClosing: {
        paintEntity.destroy();
    }

    GridLayout {
        id: mainLayout
        rows: 12
        columns: 6
        flow: GridLayout.TopToBottom
        anchors.fill: parent

        component GridRectangle : Rectangle {
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredWidth: Layout.columnSpan
            Layout.preferredHeight: Layout.rowSpan

            color: "#808080"
            border.color: Qt.darker(color)
            border.width: 1
        }

        GridRectangle {
            id: objectBlock

            Layout.row: 0
            Layout.column: 0
            Layout.rowSpan: 12
            Layout.columnSpan: 1

            ToolPanel {
                id: toolPanel
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: parent.top

                onSelectedToolChanged: {
                    if ((toolPanel.selectedTool === PaintEntity.ToolType.Select) ||
                        (toolPanel.selectedTool === PaintEntity.ToolType.Delete)) {
                        baseCuboidPicker.paintEnabled = false;
                    } else {
                        baseCuboidPicker.paintEnabled = true;
                    }

                    paintEntity.activeTool = toolPanel.selectedTool;
                }
            }

            ColorPanel {
                id: colorPanel
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: toolPanel.bottom
                anchors.topMargin: 10
                visible: true

                onSelectedColorChanged: {
                    paintEntity.setActiveColor(selectedColor);
                }
            }

            TransformPanel {
                id: transformPanel
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: colorPanel.bottom
                anchors.topMargin: 10

                visible: false

                positionLimits: Qt.vector3d(baseCuboid.xExtent, baseCuboid.yExtent, 20)

                onAngleChanged: {
                    if (paintEntity.activePaintObject !== null) {
                        paintEntity.activePaintObject.rotate(angle);
                    }
                }

                onPositionChanged: {
                    if (paintEntity.activePaintObject !== null) {
                        paintEntity.activePaintObject.move(position);
                    }
                }
            }

            Item {
                id: objectsPanels
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: transformPanel.bottom

                property var currentHeight;

                CommonObjectPanel {
                    id: commonObjectPanel
                    anchors.left: parent.left
                    anchors.right: parent.right
                    visible: false

                    onParametersChanged: {
                        if (paintEntity.activePaintObject !== null) {
                            paintEntity.activePaintObject.update(parameters);
                        }
                    }
                }
            }
        }

        GridRectangle {
            id: viewBlock

            Layout.row: 0
            Layout.column: 1
            Layout.rowSpan: 1
            Layout.columnSpan: 5

            Row {
                id: viewButtonsRow
                spacing: 2
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.verticalCenter: parent.verticalCenter

                Button {
                    id: topViewButton
                    text: qsTr("Top View")

                    onClicked:
                        ParallelAnimation {
                            PropertyAnimation { target: camera; property: "position"; to: Qt.vector3d(0.0, 0.0, 35.0); duration: 675; }
                            PropertyAnimation { target: camera; property: "upVector"; to: Qt.vector3d(0.0, 1.0, 0.0); duration: 675; }
                    }
                }

                Button {
                    id: frontViewButton
                    text: qsTr("Front View")

                    onClicked:
                        ParallelAnimation {
                            PropertyAnimation { target: camera; property: "position"; to: Qt.vector3d(0.0, -25.0, 25.0); duration: 675; }
                            PropertyAnimation { target: camera; property: "upVector"; to: Qt.vector3d(0.0, 0.0, 1.0); duration: 675; }
                    }
                }
            }
        }

        GridRectangle {
            id: paintBlock

            Layout.row: 1
            Layout.column: 1
            Layout.rowSpan: 11
            Layout.columnSpan: 5

            Scene3D {
                id: scene3D
                anchors.fill: parent
                focus: true
                aspects: ["input", "logic"]

                Entity {
                    id: rootEntity

                    Entity {
                        id: lightEntity
                        components: [
                          PointLight {
                              color: "#ffffa7"
                              intensity: 0.5
                              enabled: true
                          },
                          Transform {
                              translation: Qt.vector3d(10, 0, 10)
                          }
                        ]
                    }

                    Camera {
                        id: camera

                        projectionType: CameraLens.PerspectiveProjection
                        fieldOfView: 45
                        nearPlane : 0.1
                        farPlane : 100
                        aspectRatio: 1
                        position: Qt.vector3d(0.0, -25.0, 25.0)
                        upVector: Qt.vector3d(0.0, 0.0, 1.0)
                        viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
                    }

                    RenderSettings {
                        id: renderSettings
                        activeFrameGraph: ForwardRenderer {
                            camera: camera
                            clearColor: "transparent"
                        }
                        pickingSettings.pickMethod: PickingSettings.TrianglePicking
                    }

                    InputSettings { id: inputSettings }

                    Entity {
                        id: baseCuboidEntity

                        CuboidMesh {
                            id: baseCuboid

                            xExtent: 35
                            yExtent: 25
                            zExtent: basePlaneZExtent
                        }

                        Transform {
                            id: baseCuboidTransform
                        }

                        DiffuseSpecularMaterial {
                            id: baseCuboidMaterial
                            ambient: "#ffffff"
                            shininess: 100
                        }

                        ObjectPicker {
                            id: baseCuboidPicker
                            hoverEnabled: true
                            dragEnabled: true
                            enabled: true
                            property bool isAddingPaintObject: false
                            property bool paintEnabled: true

                            onPressed: {
                                if (paintEnabled === true) {
                                    if (pick.buttons === Qt.LeftButton) {
                                        isAddingPaintObject = true;
                                        paintEntity.onBasePlaneMousePressed(pick.worldIntersection);
                                    }
                                }
                            }

                            onMoved: {
                                if (paintEnabled === true) {
                                    if (isAddingPaintObject === true) {
                                        paintEntity.onBasePlaneMouseMoved(pick.worldIntersection);
                                    }
                                }
                            }

                            onReleased: {
                                isAddingPaintObject = false;
                            }
                        }

                        components: [
                            baseCuboid,
                            baseCuboidMaterial,
                            baseCuboidTransform,
                            baseCuboidPicker
                        ]
                    }

                    PaintEntity {
                        id: paintEntity

                        function hideObjectControls() {
                            for (var i = 0; i < objectsPanels.visibleChildren.length; i++) {
                                objectsPanels.visibleChildren[i].visible = false;
                            }

                            transformPanel.visible = false;
                            commonObjectPanel.visible = false;
                        }

                        onActivePaintObjectChanged: {
                            hideObjectControls();

                            if (activePaintObject !== null) {
                                colorPanel.setCurrentItem(activePaintObject.color);

                                transformPanel.initialPosition = activePaintObject.position;
                                transformPanel.reset();
                                transformPanel.visible = true;

                                switch(activePaintObject.type) {
                                    case PaintObject.Type.Cuboid:
                                        commonObjectPanel.controlsModel = [
                                                {text: qsTr("X extent"), sliderMin: 0.1, sliderMax: baseCuboid.xExtent, sliderCurrent: activePaintObject.parameters[0]},
                                                {text: qsTr("Y extent"), sliderMin: 0.1, sliderMax: baseCuboid.yExtent, sliderCurrent: activePaintObject.parameters[1]},
                                                {text: qsTr("Z extent"), sliderMin: 0.1, sliderMax: 10, sliderCurrent: activePaintObject.parameters[2]}
                                            ];

                                        commonObjectPanel.visible = true;
                                        break;

                                    default:
                                        break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Исходный код и проект: mtPaint3D_day_5

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x