Пятый, полуюбилейный день марафона 😉 Что имеется на данный момент:
Не вижу смысла отходить от первоначального плана, который заключался в том, чтобы сначала реализовать полный функционал приложения для конкретного 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> ¶meters) = 0; virtual QVector<qreal> getParameters() = 0; // ...............
С технической точки зрения оба метода (void update(const QVector<qreal> ¶meters)
и 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> ¶meters); 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> ¶meters) { 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
'е, у которого в качестве вариантов для выбора - разные цвета. Я пока накидал всего пару-тройку вариантов, остальные добавим позже в процессе дизайнерско-оформительных процессов. Результат следующий:
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> ¶meters) = 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