Qt Quick 3D Physics - カスタムシェイプの例

さまざまなシェイプの使用例

この例では、複数のリジッドボディメッシュのロードとスポーン、そしてそれらのアニメーションをデモします。シーンは、サイコロタワー、テーブルクロス、カップ、一握りのサイコロで構成されています。カップは、スポーンしたサイコロを集めて、サイコロタワーに入れるようにアニメーションします。サイコロはテーブルクロスの上に転がり落ちます。

環境

いつものように、PhysicsWorldView3D があります。View3D には、ライトプローブをセットアップする環境があります:

environment: SceneEnvironment {
    clearColor: "white"
    backgroundMode: SceneEnvironment.SkyBox
    antialiasingMode: SceneEnvironment.MSAA
    antialiasingQuality: SceneEnvironment.High
    lightProbe: proceduralSky
}

テクスチャ

スカイボックス、テーブルクロス、サイコロの数字に使う4つのテクスチャを定義します:

Texture {
    id: proceduralSky
    textureData: ProceduralSkyTextureData {
        sunLongitude: -115
    }
}

Texture {
    id: weaveNormal
    source: "maps/weave.png"
    scaleU: 200
    scaleV: 200
    generateMipmaps: true
    mipFilter: Texture.Linear
}

Texture {
    id: numberNormal
    source: "maps/numbers-normal.png"
}

Texture {
    id: numberFill
    source: "maps/numbers.png"
    generateMipmaps: true
    mipFilter: Texture.Linear
}

シーン

カメラと指向性ライトを含むシーンを含むノードがあります:

id: scene
scale: Qt.vector3d(2, 2, 2)
PerspectiveCamera {
    id: camera
    position: Qt.vector3d(-45, 25, 60)
    eulerRotation: Qt.vector3d(-6, -33, 0)
    clipFar: 1000
    clipNear: 0.1
}

DirectionalLight {
    eulerRotation: Qt.vector3d(-45, 25, 0)
    castsShadow: true
    brightness: 1
    shadowMapQuality: Light.ShadowMapQualityHigh
    pcfFactor: 0.1
    shadowBias: 1
}

テーブルクロス

テーブルクロスを追加します。テーブルクロスは、織物テクスチャを持つモデルと、衝突用のHeightFieldShape から構成されるStaticRigidBody

StaticRigidBody {
    position: Qt.vector3d(-15, -8, 0)
    id: tablecloth

    Model {
        geometry: HeightFieldGeometry {
            id: tableclothGeometry
            extents: Qt.vector3d(150, 20, 150)
            source: "maps/cloth-heightmap.png"
            smoothShading: false
        }
        materials: PrincipledMaterial {
            baseColor: "#447722"
            roughness: 0.8
            normalMap: weaveNormal
            normalStrength: 0.7
        }
    }

    collisionShapes: HeightFieldShape {
        id: hfShape
        extents: tableclothGeometry.extents
        source: "maps/cloth-heightmap.png"
    }
}

カップ

コップは、コリジョンシェイプとして、モデルとTriangleMeshShape を持つDynamicRigidBody として定義します。eulerRotationposition プロパティはアニメーションの一部なので、ビヘイビアを持ちます。

DynamicRigidBody {
    id: diceCup
    isKinematic: true
    mass: 0
    property vector3d bottomPos: Qt.vector3d(11, 6, 0)
    property vector3d topPos: Qt.vector3d(11, 45, 0)
    property vector3d unloadPos: Qt.vector3d(0, 45, 0)
    position: bottomPos
    kinematicPivot: Qt.vector3d(0, 6, 0)
    kinematicPosition: bottomPos
    collisionShapes: TriangleMeshShape {
        id: cupShape
        source: "meshes/simpleCup.mesh"
    }
    Model {
        source: "meshes/cup.mesh"
        materials: PrincipledMaterial {
            baseColor: "#cc9988"
            roughness: 0.3
            metalness: 1
        }
    }
}

タワー

TowerはStaticRigidBody 、ModelとTriangleMeshShape

StaticRigidBody {
    id: diceTower
    x: -4
    Model {
        id: testModel
        source: "meshes/tower.mesh"
        materials: [
            PrincipledMaterial {
                baseColor: "#ccccce"
                roughness: 0.3
            },
            PrincipledMaterial {
                id: glassMaterial
                baseColor: "#aaaacc"
                transmissionFactor: 0.95
                thicknessFactor: 1
                roughness: 0.05
            }
        ]
    }
    collisionShapes: TriangleMeshShape {
        id: triShape
        source: "meshes/tower.mesh"
    }
}

サイコロ

サイコロを生成するには、ComponentとRepeater3D 。 ComponentにはDynamicRigidBodyConvexMeshShape 、Modelが含まれます。位置、色、スケール、メッシュソースは、ダイスごとにランダムに生成されます。

Component {
    id: diceComponent

    DynamicRigidBody {
        id: thisBody
        function randomInRange(min, max) {
            return Math.random() * (max - min) + min
        }

        function restore() {
            reset(initialPosition, eulerRotation)
        }

        scale: Qt.vector3d(scaleFactor, scaleFactor, scaleFactor)
        eulerRotation: Qt.vector3d(randomInRange(0, 360),
                                   randomInRange(0, 360),
                                   randomInRange(0, 360))

        property vector3d initialPosition: Qt.vector3d(11 + 1.5 * Math.cos(index/(Math.PI/4)),
                                                       diceCup.bottomPos.y + index * 1.5,
                                                       0)
        position: initialPosition

        property real scaleFactor: randomInRange(0.8, 1.4)
        property color baseCol: Qt.hsla(randomInRange(0, 1),
                                        randomInRange(0.6, 1.0),
                                        randomInRange(0.4, 0.7),
                                        1.0)

        collisionShapes: ConvexMeshShape {
            id: diceShape
            source: Math.random() < 0.25 ? "meshes/icosahedron.mesh"
                  : Math.random() < 0.5 ? "meshes/dodecahedron.mesh"
                  : Math.random() < 0.75 ? "meshes/octahedron.mesh"
                                         : "meshes/tetrahedron.mesh"
        }

        Model {
            id: thisModel
            source: diceShape.source
            receivesShadows: false
            materials: PrincipledMaterial {
                metalness: 1.0
                roughness: randomInRange(0.2, 0.6)
                baseColor: baseCol
                emissiveMap: numberFill
                emissiveFactor: Qt.vector3d(1, 1, 1)
                normalMap: numberNormal
                normalStrength: 0.75
            }
        }
    }
}

Repeater3D {
    id: dicePool
    model: 25
    delegate: diceComponent
    function restore() {
        for (var i = 0; i < count; i++) {
            objectAt(i).restore()
        }
    }
}

アニメーション

サイコロをカップからサイコロタワーに移動させるために、カップをアニメーションさせて上に移動させ、ひっくり返します。アニメーションが物理シミュレーションと同期していることを確認するために、PhysicsWorldonFrameDone シグナルに接続したAnimationController を使用します。シミュレーションの各フレームの後、経過したタイムステップでアニメーションを進行させます。

Connections {
    target: physicsWorld
    property real totalAnimationTime: 7500
    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 {
        PauseAnimation { duration: 2500 }
        PropertyAnimation {
            target: diceCup
            property: "kinematicPosition"
            to: diceCup.topPos
            duration: 2500
        }
        ParallelAnimation {
            PropertyAnimation {
                target: diceCup
                property: "kinematicEulerRotation.z"
                to: 130
                duration: 1500
            }
            PropertyAnimation {
                target: diceCup
                property: "kinematicPosition"
                to: diceCup.unloadPos
                duration: 1500
            }
        }
        PauseAnimation { duration: 1000 }
        ParallelAnimation {
            PropertyAnimation {
                target: diceCup
                property: "kinematicEulerRotation.z"
                to: 0
                duration: 1500
            }
            PropertyAnimation {
                target: diceCup
                property: "kinematicPosition"
                to: diceCup.topPos
                duration: 1500
            }
        }
        PropertyAnimation { target: diceCup; property: "kinematicPosition"; to: diceCup.bottomPos; duration: 1500 }
        PauseAnimation { duration: 2000 }
        ScriptAction { script: dicePool.restore() }
    }
}

コントローラ

最後に、キーボードを使ってカメラをコントロールできるように、WasdController

WasdController {
    keysEnabled: true
    controlledObject: camera
    speed: 0.2
}

ファイル

画像

ここに含まれる文書の著作権は、それぞれの所有者に帰属します 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。