Qt Quick 3D Physics - Joints Example

Demonstrates using joints in a physics scene.

This example demonstrates using the joints available in Quick 3D Physics. Most joint types (DistanceJoint, PrismaticJoint, RevoluteJoint, and SphericalJoint) inherit from FlexibleJoint to support soft constraints via stiffness and damping. Additionally, D6Joint provides fully configurable 6-degrees-of-freedom constraints, while FixedJoint completely locks all degrees of freedom.

The scene is a typical scene with a PhysicsWorld, a View3D with a PerspectiveCamera and a DirectionalLight:

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Qt Quick 3D Physics - Joints")

    PhysicsWorld {
        scene: viewport.scene
        running: true
    }

    View3D {
        id: viewport
        anchors.fill: parent

        environment: SceneEnvironment {
            clearColor: "#d6dbdf"
            backgroundMode: SceneEnvironment.Color
        }

        PerspectiveCamera {
            position: Qt.vector3d(0, 600, 700)
            eulerRotation: Qt.vector3d(-30, 0, 0)
            clipFar: 5000
            clipNear: 1
        }

        DirectionalLight {
            eulerRotation.x: -45
            eulerRotation.y: 45
            castsShadow: true
            brightness: 1
            shadowFactor: 50
            shadowMapQuality: Light.ShadowMapQualityHigh
        }

        Rope {
            eulerRotation: Qt.vector3d(0, 0, -90)
            position: Qt.vector3d(0, 500, 0)
        }

        SoftRope {
            eulerRotation: Qt.vector3d(0, 0, -90)
            position: Qt.vector3d(100, 500, 0)
        }

        Prismatic {
            id: prismatic
            position: Qt.vector3d(-250, 100, 0)
        }

        Revolute {
            id: revolute
            position: Qt.vector3d(200, 200, 100)
        }

        Spring {
            eulerRotation: Qt.vector3d(0, 0, 0)
            position: Qt.vector3d(0, 1, -400)
        }


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

        FrameAnimation {
            id: animator
            running: true
            onTriggered: {
                prismatic.jointRotation.z += 1
                revolute.jointRotation.x += 0.95
            }
        }
    }
}

There is a StaticRigidBody with a PlaneShape to act as the floor. There are several custom QML objects (Rope, SoftRope, Spring, Prismatic, and Revolute) demonstrating different joint configurations.

Rope {
    eulerRotation: Qt.vector3d(0, 0, -90)
    position: Qt.vector3d(0, 500, 0)
}

SoftRope {
    eulerRotation: Qt.vector3d(0, 0, -90)
    position: Qt.vector3d(100, 500, 0)
}

Prismatic {
    id: prismatic
    position: Qt.vector3d(-250, 100, 0)
}

Revolute {
    id: revolute
    position: Qt.vector3d(200, 200, 100)
}

Spring {
    eulerRotation: Qt.vector3d(0, 0, 0)
    position: Qt.vector3d(0, 1, -400)
}

The Rope object is a series of capsules and a sphere that are connected with spherical joints for the capsules and a fixed joint for the sphere.

Node {
    id: root

    SphericalJoint {
        bodyB: shape0
        positionA: root.position
        positionB: Qt.vector3d(-25, 0, 0)
    }

    SphericalJoint {
        bodyA: shape0
        bodyB: shape1
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
    }

    SphericalJoint {
        bodyA: shape1
        bodyB: shape2
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
    }

    SphericalJoint {
        bodyA: shape2
        bodyB: shape3
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
    }

    SphericalJoint {
        bodyA: shape3
        bodyB: shape4
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
    }

    SphericalJoint {
        bodyA: shape4
        bodyB: shape5
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
    }

    FixedJoint {
        bodyA: shape5
        bodyB: sphere
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(0, 0, 0)
    }

    // Neighboring links touch exactly at their joints, so each body ignores collisions
    // with its immediate neighbors to avoid fighting the joint with contact forces.
    DynamicRigidBody {
        id: shape0
        position: Qt.vector3d(25, 0, 0)
        filterGroup: 0
        filterIgnoreGroups: 0b0000010
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }

    DynamicRigidBody {
        id: shape1
        position: Qt.vector3d(75, 0, 0)
        filterGroup: 1
        filterIgnoreGroups: 0b0000101
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }

    DynamicRigidBody {
        id: shape2
        position: Qt.vector3d(125, 0, 0)
        filterGroup: 2
        filterIgnoreGroups: 0b0001010
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }

    DynamicRigidBody {
        id: shape3
        position: Qt.vector3d(175, 0, 0)
        filterGroup: 3
        filterIgnoreGroups: 0b0010100
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }

    DynamicRigidBody {
        id: shape4
        position: Qt.vector3d(225, 0, 0)
        filterGroup: 4
        filterIgnoreGroups: 0b0101000
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }

    DynamicRigidBody {
        id: shape5
        position: Qt.vector3d(275, 0, 0)
        filterGroup: 5
        filterIgnoreGroups: 0b1010000
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }

    DynamicRigidBody {
        id: sphere
        position: Qt.vector3d(300, 0, 0)
        scale: Qt.vector3d(0.5, 0.5, 0.5)
        filterGroup: 6
        filterIgnoreGroups: 0b0100000
        collisionShapes: SphereShape {}
        Model {
            source: "#Sphere"
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }
}

The SoftRope object demonstrates how flexible joints use stiffness and damping properties to create spring-like soft connections.

Node {
    id: root

    property real stiffness : 15000
    property real damping : 500

    SphericalJoint {
        bodyB: shape0
        positionA: root.position
        positionB: Qt.vector3d(-25, 0, 0)
    }

    PrismaticJoint {
        bodyA: shape0
        bodyB: shape1
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
        stiffness: root.stiffness
        damping: root.damping
        lowerLimit: -20
        upperLimit: 0
    }

    SphericalJoint {
        bodyA: shape1
        bodyB: shape2
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
    }

    PrismaticJoint {
        bodyA: shape2
        bodyB: shape3
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(-25, 0, 0)
        stiffness: root.stiffness
        damping: root.damping
        lowerLimit: -20
        upperLimit: 0
    }

    FixedJoint {
        bodyA: shape3
        bodyB: sphere
        positionA: Qt.vector3d(25, 0, 0)
        positionB: Qt.vector3d(0, 0, 0)
    }

    // Neighboring links touch exactly at their joints, so each body ignores collisions
    // with its immediate neighbors to avoid fighting the joint with contact forces.
    DynamicRigidBody {
        id: shape0
        position: Qt.vector3d(25, 0, 0)
        filterGroup: 0
        filterIgnoreGroups: 0b00010
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blueviolet"
            }
        }
    }

    DynamicRigidBody {
        id: shape1
        position: Qt.vector3d(75, 0, 0)
        filterGroup: 1
        filterIgnoreGroups: 0b00101
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blueviolet"
            }
        }
    }

    DynamicRigidBody {
        id: shape2
        position: Qt.vector3d(125, 0, 0)
        filterGroup: 2
        filterIgnoreGroups: 0b01010
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blueviolet"
            }
        }
    }

    DynamicRigidBody {
        id: shape3
        position: Qt.vector3d(175, 0, 0)
        filterGroup: 3
        filterIgnoreGroups: 0b10100
        collisionShapes: CapsuleShape {
            diameter: 10
            height: 40
        }
        Model {
            geometry: CapsuleGeometry {
                diameter: 10
                height: 40
            }
            materials: PrincipledMaterial {
                baseColor: "blueviolet"
            }
        }
    }

    DynamicRigidBody {
        id: sphere
        position: Qt.vector3d(200, 0, 0)
        scale: Qt.vector3d(0.5, 0.5, 0.5)
        filterGroup: 4
        filterIgnoreGroups: 0b01000
        collisionShapes: SphereShape {}

        Model {
            source: "#Sphere"
            materials: PrincipledMaterial {
                baseColor: "blueviolet"
            }
        }
    }
}

The Spring object shows how to use D6Joint to constrain motion across 6 degrees of freedom with custom linear and angular spring parameters.

Node {
    id: root

    DynamicRigidBody {
        id: baseStand
        position: Qt.vector3d(0, 0, 0)

        collisionShapes: BoxShape {
            extents: Qt.vector3d(50, 10, 50)
        }

        Model {
            source: "#Cube"
            scale: Qt.vector3d(0.50, 0.10, 0.50)

            materials: PrincipledMaterial {
                baseColor: "darkslategray"
            }
        }
    }

    DynamicRigidBody {
        id: floatingSphere
        position: Qt.vector3d(0, 102.5, 0)

        collisionShapes: SphereShape {
            diameter: 20
        }

        Model {
            source: "#Sphere"
            // scale: Qt.vector3d(0.2, 0.2, 0.2)
            materials: PrincipledMaterial {
                baseColor: "cornflowerblue"
                roughness: 0.2
            }
        }
    }

    // D6 Joint acting as a soft spring wire
    D6Joint {
        bodyA: baseStand
        bodyB: floatingSphere

        positionA: Qt.vector3d(0, 2.5, 0)
        positionB: Qt.vector3d(0, -100, 0)

        xMotion: D6Joint.Locked
        yMotion: D6Joint.Locked
        zMotion: D6Joint.Locked

        // Allow omnidirectional rotation
        twistMotion: D6Joint.Limited
        swingMotionY: D6Joint.Limited
        swingMotionZ: D6Joint.Limited

        twistLimitLower: -0.07    // ~-4 degrees in radians
        twistLimitUpper: 0.07     // ~+4 degrees in radians
        swingLimitAngleY: 0.087   // ~5 degrees in radians
        swingLimitAngleZ: 0.087   // ~5 degrees in radians

        angularStiffness: 2550000.0
        angularDamping: 2500.0
    }
}

The Prismatic object consists of two bars locked in a prismatic joint along the x-axis of both of them. With this joint the smaller rod can move freely along this x-axis until it reaches its upper and lower constraint limit.

Node {
    id: root
    property vector3d jointRotation : Qt.vector3d(0, 0, 90)

    PrismaticJoint {
        bodyA: prismaticBoxA
        bodyB: prismaticBoxB
        lowerLimit: -200
        upperLimit: 0
        positionA: Qt.vector3d(100, 0, 0)
        positionB: Qt.vector3d(-100, 0, 0)
    }

    DynamicRigidBody {
        id: prismaticBoxA
        position: Qt.vector3d(0, 200, 0)
        eulerRotation: root.jointRotation
        kinematicPosition: Qt.vector3d(0, 200, 0)
        kinematicEulerRotation: root.jointRotation
        isKinematic: true
        scale: Qt.vector3d(2, 0.5, 0.5)
        collisionShapes: BoxShape {}
        Model {
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "yellow"
            }
        }
    }

    DynamicRigidBody {
        id: prismaticBoxB
        position: Qt.vector3d(200 * Math.cos(root.jointRotation.z * Math.PI / 180),
                               200 + 200 * Math.sin(root.jointRotation.z * Math.PI / 180),
                               0)
        eulerRotation: root.jointRotation
        scale: Qt.vector3d(2, 0.4, 0.4)
        collisionShapes: BoxShape {}
        Model {
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }
}

The Revolute object consists of two bars rotated by 90 degrees and locked in a revolute joint.

Node {
    id: root
    property vector3d jointRotation : Qt.vector3d(0, 90, 0)

    RevoluteJoint {
        bodyA: revoluteBoxA
        bodyB: revoluteBoxB
        positionA: Qt.vector3d(100, 0, 0)
        positionB: Qt.vector3d(100, 0, 0)
        orientationB: Quaternion.fromEulerAngles(0, 0, 90)
        angularLimitLower: -Math.PI / 4
        angularLimitUpper: Math.PI / 4
        enableAngularLimit: true
    }

    DynamicRigidBody {
        id: revoluteBoxA
        position: Qt.vector3d(0, 0, 0)
        eulerRotation: root.jointRotation
        kinematicPosition: Qt.vector3d(0, 0, 0)
        kinematicEulerRotation: root.jointRotation
        isKinematic: true
        scale: Qt.vector3d(2, 0.5, 0.5)
        collisionShapes: BoxShape {}
        Model {
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "yellow"
            }
        }
    }

    DynamicRigidBody {
        id: revoluteBoxB
        position: Qt.vector3d(0, 100, -100)
        eulerRotation: Qt.vector3d(0, 90, -90)
        scale: Qt.vector3d(2, 0.5, 0.5)
        collisionShapes: BoxShape {}
        Model {
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "blue"
            }
        }
    }
}

Going back to the main scene there is a FrameAnimation object that rotates the kinematic prismatic and revolute joints.

FrameAnimation {
    id: animator
    running: true
    onTriggered: {
        prismatic.jointRotation.z += 1
        revolute.jointRotation.x += 0.95
    }
}

With all these moving parts we can see how the different joints interact in a physics scene.

Files:

© 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.