如何在 Qt3D 中创建 Undo/Redo 操作?

How to create Undo/Redo operations in Qt3D?

我在 QML 中使用 qt3d 创建了一些实体。例如,此代码显示一个 Scene3D 元素声明 RootEntity 这是另一个包含场景图的 QML 元素:

Scene3D
{
    id : scene3d
    anchors.fill: parent
    focus: true
    aspects: ["render", "logic", "input"]
    hoverEnabled: true
    cameraAspectRatioMode: Scene3D.AutomaticAspectRatio


    antialiasing: true

    RootEntity
    {
        id:root
    }

}

RootEntity.qml:

Entity {
id:root

property double x : 0.0


Camera {
    id: mainCamera
    projectionType: CameraLens.PerspectiveProjection
    fieldOfView: 45
    aspectRatio: 16/9
    nearPlane : 0.1
    farPlane : 1000.0
    position: Qt.vector3d(0.0, 4.49373, -3.78577)
    upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
    viewCenter: Qt.vector3d(0.0, 0.5, 0.0)

}

OrbitCameraController
{
    id: mainCameraController
    camera: mainCamera
}

components: [
    RenderSettings {

        Viewport {
            normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
            RenderSurfaceSelector {
                CameraSelector {
                    id: cameraSelector
                    camera: mainCamera
                    FrustumCulling {
                        ClearBuffers {
                            buffers: ClearBuffers.AllBuffers
                            clearColor: "#444449"
                            NoDraw {}
                        }
                        LayerFilter {
                            filterMode: LayerFilter.DiscardAnyMatchingLayers
                            layers: [topLayer]
                        }
                        LayerFilter {
                            filterMode: LayerFilter.AcceptAnyMatchingLayers
                            layers: [topLayer]
                            ClearBuffers {
                                buffers: ClearBuffers.DepthBuffer
                            }
                        }
                    }
                }
            }
        }      
    },
    InputSettings {}
]

Layer {
    id: topLayer
    recursive: true
}

ListModel {
    id: entityModel
    ListElement { x:0;y:0;z:0 }
}

NodeInstantiator
{
    id:instance

    model: entityModel

    delegate: Entity {
        id: sphereEntity
        components: [
            SphereMesh
            {
                id:sphereMesh
                radius: 0.3
            },

            PhongMaterial
            {
                id: materialSphere
                ambient:"red"
            },

            Transform {
                id: transform
                translation:Qt.vector3d(x, y, z)
            }
        ]
    }
}

MouseDevice
{
    id: mouseDev
}

MouseHandler
{
    id: mouseHandler
    sourceDevice: mouseDev

    onPressed:
    {
        x++;
        entityModel.append({"x":x,"y":0.0,"z": Math.random()})
    }
}
}

在我的 Scene3D 中单击鼠标时,会显示一个球体。

我不知道如何删除特定实体或通过按 Ctrl+Z 和创建 undo/redo 效果Ctrl+Shift+Z 在 Qt3d 中。 谢谢。

一种方法是维护 Qt.vector3d 元素的全局列表,并使用它来记录通过“撤消”操作删除的球体的位置:

  • 当用户点击CTRL+Z时,创建一个新的Qt.vector3d对象来存储最后一个球体的位置渲染(即最后附加到 entityModel 的位置)并将该位置添加到 3d 向量的全局列表中;
  • 然后,要从屏幕上删除球体,请使用需要删除的球体的索引调用 entityModel.remove()

“重做”操作恰恰相反:

  • 当用户点击 CTRL+Y 时,全局 3d 矢量列表的最后一个元素保存最新球体的位置删除:将此位置附加到 entityModel 以便可以再次渲染球体;
  • 然后,记得从全局列表中删除这个位置,以便下一个撤消操作可以渲染不同的球体;

RootEntity.qml:

import QtQuick 2.0

import QtQml.Models 2.15

import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Extras 2.12
import Qt3D.Input 2.12

Entity {
    id: root

    // global list of Qt.vector3d elements that store the location of the spheres that are removed
    property variant removedSpheres : []

    // x-coordinate of the next sphere that will be added
    property double x : 0.0

    Camera {
        id: mainCamera
        projectionType: CameraLens.PerspectiveProjection
        fieldOfView: 45
        aspectRatio: 16/9
        nearPlane : 0.1
        farPlane : 1000.0
        position: Qt.vector3d(0.0, 4.49373, -3.78577)
        upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
        viewCenter: Qt.vector3d(0.0, 0.5, 0.0)
    }

    OrbitCameraController {
        id: mainCameraController
        camera: mainCamera
    }

    components: [
        RenderSettings {

            Viewport {
                normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
                RenderSurfaceSelector {
                    CameraSelector {
                        id: cameraSelector
                        camera: mainCamera
                        FrustumCulling {
                            ClearBuffers {
                                buffers: ClearBuffers.AllBuffers
                                clearColor: "#444449"
                                NoDraw {}
                            }
                            LayerFilter {
                                filterMode: LayerFilter.DiscardAnyMatchingLayers
                                layers: [topLayer]
                            }
                            LayerFilter {
                                filterMode: LayerFilter.AcceptAnyMatchingLayers
                                layers: [topLayer]
                                ClearBuffers {
                                    buffers: ClearBuffers.DepthBuffer
                                }
                            }
                        }
                    }
                }
            }
        },
        InputSettings {}
    ]

    Layer {
        id: topLayer
        recursive: true
    }

    ListModel {
        id: entityModel

        ListElement { x: 0; y: 0; z: 0 }
    }

    NodeInstantiator {
        id: instance

        model: entityModel

        delegate: Entity {
            id: sphereEntity

            components: [
                SphereMesh { id:sphereMesh; radius: 0.3 },

                PhongMaterial { id: materialSphere; ambient:"red" },

                Transform { id: transform; translation:Qt.vector3d(x, y, z) }
            ]
        }
    }

    MouseDevice {
        id: mouseDev
    }

    MouseHandler {
        id: mouseHandler
        sourceDevice: mouseDev

        onPressed:
        {
            if (mouse.button === Qt.LeftButton)
            {
                console.log("LeftButton: new sphere")

                // add new sphere
                entityModel.append( {"x" : ++root.x, "y" : 0.0, "z" : Math.random()} )
            }

            if (mouse.button === Qt.MiddleButton)
            {
                console.log("MiddleButton: clear spheres")

                // removes all spheres (can't be undone)
                root.x = 0;
                entityModel.clear();
                removedSpheres.length = 0;
            }
        }
    }

    KeyboardDevice {
        id: keyboardDev
    }

    KeyboardHandler {
        id: keyboardHandler
        sourceDevice: keyboardDev
        focus: true

        onPressed: {
            // handle CTRL+Z: undo
            if (event.key === Qt.Key_Z && (event.modifiers & Qt.ControlModifier))
            {
                console.log("CTRL+Z")

                // remove the last sphere added to the screen
                let lastIdx = entityModel.count - 1;
                if (lastIdx >= 0)
                {
                    // save sphere position before removal
                    removedSpheres.push(Qt.vector3d(entityModel.get(lastIdx).x, entityModel.get(lastIdx).y, entityModel.get(lastIdx).z));

                    // remove sphere from the model
                    entityModel.remove(lastIdx);
                }
            }

            // handle CTRL+Y: redo
            if (event.key === Qt.Key_Y && (event.modifiers & Qt.ControlModifier))
            {
                console.log("CTRL+Y")

                // add the last sphere removed back into the model
                if (removedSpheres.length > 0)
                {
                    // add the sphere
                    let lastIdx = removedSpheres.length - 1;
                    entityModel.append( {"x" : removedSpheres[lastIdx].x, "y" : removedSpheres[lastIdx].y, "z" : removedSpheres[lastIdx].z} )

                    // erase the last item added to removedSpheres
                    removedSpheres.pop()
                }
            }
        }
    }

}