Qt Quick 3D 物理引擎 - 复合形状示例

演示如何使用复杂碰撞形状。

该示例演示了如何使用多个碰撞形状来创建用于碰撞检测的复杂对象。场景由一个绿色静态平面和一系列相互连接的链接组成。一开始,模拟处于禁用状态。经过一段时间或用户按下空格键后,模拟将开始。将开始一个动画,使最左边和最右边的链接沿着水平方向来回移动。

设置

我们像往常一样需要添加我们的 PhysicsWorld

PhysicsWorld {
    id: physicsWorld
    enableCCD: true
    maximumTimestep: 20
    scene: viewport.scene
}

在具有环境、相机和灯光的常规设置中操作

environment: SceneEnvironment {
    antialiasingMode: SceneEnvironment.MSAA
    backgroundMode: SceneEnvironment.Color
    clearColor: "lightblue"
}

focus: true

PerspectiveCamera {
    id: camera
    position: Qt.vector3d(0, 900, 1500)
    eulerRotation: Qt.vector3d(-10, 0, 0)
    clipFar: 15500
    clipNear: 1
}

DirectionalLight {
    eulerRotation.x: -45
    eulerRotation.y: 45
    castsShadow: true
    brightness: 1.5
    shadowFactor: 15
    shadowFilter: 10
    shadowMapFar: 100
    shadowBias: -0.01
    shadowMapQuality: Light.ShadowMapQualityVeryHigh
}

物理对象

我们有一个常规静态平面

StaticRigidBody {
    position: Qt.vector3d(0, -100, 0)
    eulerRotation: Qt.vector3d(-90, 0, 0)
    collisionShapes: PlaneShape {}
    Model {
        source: "#Rectangle"
        scale: Qt.vector3d(500, 500, 1)
        materials: DefaultMaterial {
            diffuseColor: "green"
        }
        castsShadows: false
        receivesShadows: true
    }
}

然后我们创建链接的实例。

MeshLink {
    id: leftLink
    isKinematic: true
    property vector3d startPos: Qt.vector3d(-6 * viewport.ringDistance,
                                            viewport.ringY,
                                            0)
    property vector3d startRot: Qt.vector3d(90, 0, 0)
    kinematicPosition: startPos
    position: startPos
    kinematicEulerRotation: startRot
    eulerRotation: startRot
    color: "red"
}

CapsuleLink {
    position: Qt.vector3d(-5 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(-4 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(-3 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

MeshLink {
    position: Qt.vector3d(-2 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(-1 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

CapsuleLink {
    position: Qt.vector3d(0, viewport.ringY, 0)
}

MeshLink {
    position: Qt.vector3d(1 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

MeshLink {
    position: Qt.vector3d(2 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(3 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

MeshLink {
    position: Qt.vector3d(4 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

CapsuleLink {
    position: Qt.vector3d(5 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    id: rightLink
    isKinematic: true
    property vector3d startPos: Qt.vector3d(6 * viewport.ringDistance,
                                            viewport.ringY,
                                            0)
    property vector3d startRot: Qt.vector3d(90, 0, 0)
    kinematicPosition: startPos
    position: startPos
    kinematicEulerRotation: startRot
    eulerRotation: startRot
    color: "red"
}

最左侧的第一链接的 isKinematic 属性设置为 true,这样我们就可以通过动画来控制它。由于它是运动学对象,我们需要设置 kinematicPositionkinematicRotation 属性。我们通过动画它来动画化 kinematicPosition 属性。其他链接之间有一定的间隔。

为了获得平滑的动画,使其正好跟随物理模拟,我们使用一个AnimationController,它连接到 PhysicsWorldonFrameDone 信号。这样,如果有帧丢失,导致模拟变慢,动画也会相应地减速。我们使用一个带有四个 NumberAnimationSequentialAnimation 来移动最左边和最右边的环。这是动画的 QML 代码

Connections {
    target: physicsWorld
    property real totalAnimationTime: 12000
    function onFrameDone(timeStep) {
        let progressStep = timeStep / totalAnimationTime
        animationController.progress += progressStep
        if (animationController.progress >= 1) {
            animationController.completeToEnd()
            animationController.reload()
            animationController.progress = 0
        }
    }
}

AnimationController {
    id: animationController
    animation: SequentialAnimation {
        NumberAnimation {
            target: leftLink
            property: "kinematicPosition.x"
            to: 3 * viewport.ringDistance
            from: -6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
        NumberAnimation {
            target: leftLink
            property: "kinematicPosition.x"
            from: 3 * viewport.ringDistance
            to: -6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
        NumberAnimation {
            target: rightLink
            property: "kinematicPosition.x"
            to: -3 * viewport.ringDistance
            from: 6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
        NumberAnimation {
            target: rightLink
            property: "kinematicPosition.x"
            from: -3 * viewport.ringDistance
            to: 6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
    }
}

有趣的部分在于 Mesh 和 Capsule Links 文件内部的情况。让我们分别看看每一个。

DynamicRigidBody {
    scale: Qt.vector3d(100, 100, 100)
    property color color: "white"
    PrincipledMaterial {
        id: _material
        baseColor: color
        metalness: 1.0
        roughness: 0.5
    }

    Model {
        source: "meshes/ring.mesh"
        materials: [_material]
    }

    collisionShapes: [
        ConvexMeshShape {
            source: "meshes/segmentedRing_001.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_002.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_003.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_004.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_005.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_006.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_007.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_008.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_009.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_010.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_011.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_012.mesh"
        }
    ]
}

网格链接是具有模型和材料的动态刚体。模型从网格文件中加载网格。我们还有一个碰撞形状列表,这些形状组合在一起形成一个复合形状,用于碰撞检测。每个形状都是一个凸网格形状,它从源文件中加载网格。凸形状基本上是形状,其中形状内的任意两点之间的线总是在形状内部。

当开启调试模式并仔细观察时,我们可以看到碰撞形状是如何形成复合碰撞形状的。

DynamicRigidBody {
    property real len: 170
    property real w: 17
    PrincipledMaterial {
        id: material3
        baseColor: "yellow"
        metalness: 1.0
        roughness: 0.5
    }
    Node {
        opacity: 1
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            eulerRotation.z: 90
            y: -len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            eulerRotation.z: 90
            y: len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            x: len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            x: -len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: -len / 2
            y: -len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: -len / 2
            y: len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: len / 2
            y: -len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: len / 2
            y: len / 2
        }
    }
    collisionShapes: [
        CapsuleShape {
            y: -len / 2
            height: len
            diameter: w
        },
        CapsuleShape {
            y: len / 2
            height: len
            diameter: w
        },
        CapsuleShape {
            x: -len / 2
            eulerRotation.z: 90
            height: len
            diameter: w
        },
        CapsuleShape {
            x: len / 2
            eulerRotation.z: 90
            height: len
            diameter: w
        }
    ]
}

胶囊链接是一种具有某些相同模型的动态刚体。这个链接由多个圆柱和球体组成。和网格链接一样,我们有一个碰撞形状列表。这次每个形状都是一个胶囊形状。

当开启调试模式并仔细观察时,我们可以看到碰撞形状是如何形成复合碰撞形状的。

文件

© 2024 Qt公司有限公司。本文件中包含的文档贡献物的版权属于其各自的所有者。本文件中的文档根据自由软件基金会发布的 GNU自由文档许可证版本1.3 的条款进行许可。Qt及其相关商标是芬兰及/或其他国家的Qt公司有限公司的商标。所有其他商标均属于其各自的所有者。