Qt Quick 3D 物理引擎 - 自定义形状示例
演示了使用不同形状的应用。
此示例演示了加载和生成多个刚体网格以及对他们进行动画处理。场景包括一个骰塔、桌布、一个杯子,和一些骰子。杯子会进行动画处理来收集生成的骰子并将它们放入骰塔中,然后骰子会从骰塔滚下并在桌布上滚动。
环境
一如既往,我们有一个PhysicsWorld和一个View3D。在View3D中,我们有我们的环境,该环境设置了灯光探针。
environment: SceneEnvironment { clearColor: "white" backgroundMode: SceneEnvironment.SkyBox antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High lightProbe: proceduralSky }
纹理
我们定义了四个纹理,这些纹理将被用于天空盒、桌布和骰子上的数字。
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.ShadowMapQualityVeryHigh }
桌布
我们添加了桌布,它是一个由一个带有编织纹理的模型和一个用于碰撞的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。它在其eulerRotation
和position
属性上有行为,因为这些属性属于动画的一部分。
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 } } }
塔
这座塔只是一个具有TriangleMeshShape用于碰撞的StaticRigidBody和模型。
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" } }
骰子
为了生成骰子,我们使用了一个组件和Repeater3D。该组件包含了一个具有ConvexMeshShape和模型的DynamicRigidBody。每个骰子的位置、颜色、比例和网格源都是随机生成的。
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 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() } } }
动画
为了将骰子从杯子移动到骰子塔,我们通过动画使杯子向上移动,然后将其倾斜。为确保动画与物理模拟保持同步,我们使用了一个AnimationController,并将其连接到PhysicsWorld的onFrameDone
信号。在每一帧模拟之后,我们使用经过的时间步进行动画推进。
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 }
文件
- customshapes/CMakeLists.txt
- customshapes/customshapes.pro
- customshapes/main.cpp
- customshapes/main.qml
- customshapes/qml.qrc
- customshapes/resources.qrc
图像
© 2024 Qt公司有限公司。此文档中包含的贡献的文档版权属于各自的拥有者。所提供的文档根据自由软件基金会发布的GNU Free Documentation License版本1.3的条款进行许可。Qt及相关标志是芬兰及/或全球的Qt公司有限公司的商标。所有其他商标均属于其各自的所有者。