Sur cette page

Qt Quick 3D Physics - Exemple de formes composées

Démonstration de l'utilisation de formes de collision complexes.

Capture d'écran représentant un paysage ainsi que plusieurs cadres carrés et anneaux reliés entre eux

Cet exemple montre comment utiliser plusieurs formes de collision pour créer des objets complexes en vue de la détection des collisions. La scène se compose d'un plan statique vert et d'une série de maillons reliés entre eux. Au début, la simulation est désactivée. Après un certain temps ou lorsque l'utilisateur appuie sur la touche espace, la simulation démarre. Une animation se lancera, provoquant un mouvement horizontal de va-et-vient des maillons situés à l'extrême gauche et à l'extrême droite.

Configuration

Comme d’habitude, nous devons ajouter notre projet « PhysicsWorld » :

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

Nous procédons à la configuration habituelle comprenant un environnement, une caméra et des lumières :

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
    shadowMapQuality: Light.ShadowMapQualityHigh
}

Objets physiques

Nous disposons de notre plan statique habituel :

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: PrincipledMaterial {
            baseColor: "green"
        }
        castsShadows: false
        receivesShadows: true
    }
}

Ensuite, nous créons des instances de nos liaisons.

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"
}

Le premier maillon, situé à gauche, a sa propriété « isKinematic » définie sur « true » afin que nous puissions le contrôler via une animation. Comme il s’agit d’un objet cinématique, nous devons définir les propriétés « kinematicPosition » et « kinematicRotation ». Nous l’animons en animant la propriété « kinematicPosition ». Les autres maillons sont instanciés avec un certain espacement entre eux.

Pour obtenir une animation fluide qui suive exactement la simulation physique, nous utilisons un « AnimationController » que nous connectons au signal « onFrameDone » de la propriété « PhysicsWorld ». De cette manière, s’il venait à y avoir des pertes d’images ralentissant la simulation, l’animation ralentirait en conséquence. Nous utilisons un « SequentialAnimation » avec quatre « NumberAnimation » pour faire aller et venir les anneaux situés à l’extrême gauche et à l’extrême droite. Voici le code QML de l’animation :

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
        }
    }
}

Ce qui est intéressant, c’est ce qui se passe à l’intérieur des fichiers Mesh Link et Capsule Link. Examinons-les un par un.

DynamicRigidBody {
    id: root
    scale: Qt.vector3d(100, 100, 100)
    property color color: "white"
    PrincipledMaterial {
        id: _material
        baseColor: root.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"
        }
    ]
}

Le lien Mesh est un corps rigide dynamique doté d’un modèle et d’un matériau. Le modèle charge le maillage à partir d’un fichier de maillage. Nous disposons également d’une liste de formes de collision qui, une fois combinées, forment une forme composite pour la détection des collisions. Chaque forme est une forme « Convex Mesh » qui charge le maillage à partir d’un fichier source. Une forme convexe est essentiellement une forme dans laquelle la ligne reliant deux points quelconques à l’intérieur de la forme se trouve toujours à l’intérieur de celle-ci.

Si l’on y regarde de plus près lorsque le mode débogage est activé, voici comment les formes de collision forment la forme de collision composite :

DynamicRigidBody {
    id: root
    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(root.w / 100, root.len / 100, root.w / 100)
            eulerRotation.z: 90
            y: -root.len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(root.w / 100, root.len / 100, root.w / 100)
            eulerRotation.z: 90
            y: root.len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(root.w / 100, root.len / 100, root.w / 100)
            x: root.len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(root.w / 100, root.len / 100, root.w / 100)
            x: -root.len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(root.w / 100, root.w / 100, root.w / 100)
            x: -root.len / 2
            y: -root.len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(root.w / 100, root.w / 100, root.w / 100)
            x: -root.len / 2
            y: root.len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(root.w / 100, root.w / 100, root.w / 100)
            x: root.len / 2
            y: -root.len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(root.w / 100, root.w / 100, root.w / 100)
            x: root.len / 2
            y: root.len / 2
        }
    }
    collisionShapes: [
        CapsuleShape {
            y: -root.len / 2
            height: root.len
            diameter: root.w
        },
        CapsuleShape {
            y: root.len / 2
            height: root.len
            diameter: root.w
        },
        CapsuleShape {
            x: -root.len / 2
            eulerRotation.z: 90
            height: root.len
            diameter: root.w
        },
        CapsuleShape {
            x: root.len / 2
            eulerRotation.z: 90
            height: root.len
            diameter: root.w
        }
    ]
}

Le « Capsule Link » est un corps rigide dynamique composé de plusieurs modèles partageant le même matériau. Ce lien est constitué de plusieurs cylindres et sphères. À l’instar du « Mesh Link », il comporte une liste de formes de collision. Cette fois-ci, chaque forme est une « Capsule Shape ».

Si l’on y regarde de plus près lorsque le mode débogage est activé, voici comment les formes de collision forment la forme de collision composite.

Capture d'écran d'un rendu 3D montrant deux anneaux reliés par un cadre carré et leurs corps rigides

Fichiers :

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.