
cpp
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Extras 2.0
import Qt3D.Examples 2.0
import QtQuick 2.5 as Quick
import QtQuick.Scene3D 2.0
Entity
{
id: carModelEntity
property bool hidden: true
property Scene3D scene
property real carRotation: 0.0
property var previousComponent: undefined
property var previousMaterial
property vector3d defaultCameraPosition: Qt.vector3d(0.0, 10.0, 30.0)
property vector3d defaultLightPosition: Qt.vector3d(0.0, 30.0, 11.0)
property vector3d lightPosition: defaultLightPosition
property vector3d cameraPos: defaultCameraPosition
property vector3d lightPos: defaultLightPosition
property real lightPosMultiplier: 1.75
property int door_left: 1
property int door_right: 2
property int door_trunk: 4
property int door_hood: 8
property bool highlighting: false
property bool doorAction: false
property int highlightType: 0
property int defaultHighlight: 99
// Preset camera positions for highlights
// Light positions can use the same vectors, but with a multiplier to move it further or closer
// Lamp highlights
property vector3d positionFrontLeftHigh: Qt.vector3d(5.0, 4.0, 15.0) // Left headlight
property vector3d positionFrontRightHigh: Qt.vector3d(-5.0, 4.0, 15.0) // Right headlight
property vector3d positionFrontLeftLow: Qt.vector3d(3.0, 2.0, 15.0) // Left day light
property vector3d positionFrontRightLow: Qt.vector3d(-3.0, 2.0, 15.0) // Right day light
property vector3d positionRearLeft: Qt.vector3d(5.0, 5.0, -15.0) // Left tail light
property vector3d positionRearRight: Qt.vector3d(-5.0, 5.0, -15.0) // Right tail light
// Tire highlights
property vector3d positionLeftRear: Qt.vector3d(10.0, 2.0, -12.5)
property vector3d positionLeftFront: Qt.vector3d(10.0, 2.0, 12.5)
property vector3d positionRightRear: Qt.vector3d(-10.0, 2.0, -12.5)
property vector3d positionRightFront: Qt.vector3d(-10.0, 2.0, 12.5)
// Door highlights
property vector3d positionLeft: Qt.vector3d(35.0, 10.0, 0.0) // Doors on the left
property vector3d positionRight: Qt.vector3d(-35.0, 10.0, 0.0) // Doors on the right
property vector3d positionTop: Qt.vector3d(0.0, 40.0, 1.0) // Doors on both sides
property vector3d positionBack: Qt.vector3d(0.0, 20.0, -20.0) // Trunk
property vector3d positionFront: Qt.vector3d(0.0, 20.0, 20.0) // Hood
// Original
property color bodyColor: Qt.rgba(0.6270588, 0.04137255, 0.04137255, 1.0)
property color interiorColor: Qt.rgba(0.17, 0.17, 0.18, 1.0)
property color highlightColor: "orange"
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: scene.width / scene.height
nearPlane: 0.1
farPlane: 100.0
position: defaultCameraPosition
upVector: Qt.vector3d(0.0, 1.0, 0.0)
viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
}
Entity {
components: [
Transform {
translation: lightPosition
},
PointLight {
color: "white"
intensity: 1.0
}
]
}
RenderSettings {
Viewport {
RenderSurfaceSelector {
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
NoDraw { } // Just clear
}
CameraSelector {
camera: camera
NoDraw {
enabled: hidden
}
}
}
}
}
// Materials for the parts that need highlighting
PhongMaterial {
id: bodyMaterial
ambient: Qt.rgba(0.05, 0.05, 0.05, 1.0)
diffuse: bodyColor
specular: Qt.rgba(0.7686275, 0.6196079, 0.3568628, 1.0)
shininess: 164
}
PhongMaterial {
id: bodyMaterialHighlight
ambient: Qt.rgba(0.05, 0.05, 0.05, 1.0)
diffuse: highlightColor
shininess: 164
}
PhongMaterial {
id: tireMaterial
ambient: Qt.rgba(0.05, 0.05, 0.05, 1.0)
specular: Qt.rgba(0.594, 0.594, 0.594, 1.0)
diffuse: "black"
shininess: 51
}
PhongMaterial {
id: tireMaterialHighlight
ambient: Qt.rgba(0.05, 0.05, 0.05, 1.0)
specular: Qt.rgba(0.594, 0.594, 0.594, 1.0)
diffuse: highlightColor
shininess: 51
}
DiffuseMapMaterial {
id: lampsMaterial
ambient: Qt.rgba(0.75, 0.75, 0.75, 1.0)
specular: Qt.rgba(0.279, 0.279, 0.279, 1.0)
diffuse: TextureLoader { source: "qrc:/Map11.jpg" }
shininess: 31
}
PhongAlphaMaterial {
id: transparentGlassMaterial
diffuse: Qt.rgba(0.1608937, 0.16512, 0.154057, 1.0)
specular: Qt.rgba(1.0, 1.0, 1.0, 1.0)
alpha: 0.75
shininess: 33
}
PhongMaterial {
id: interiorMaterial
ambient: "black"
diffuse: interiorColor
shininess: 30
}
SceneHelper {
id: sceneHelper
}
Entity {
id: carModel
Transform {
id: carTransform
matrix: {
var m = Qt.matrix4x4()
m.rotate(carRotation, Qt.vector3d(0, 1, 0))
m.rotate(-90, Qt.vector3d(1, 0, 0))
m.scale(1.35)
return m
}
}
SceneLoader {
id: modelLoader
source: "qrc:/sportscar_15k.qgltf"
property var lampParts: [ "headlight_right", "headlight_left", "daylight_right",
"daylight_left", "taillight_left", "taillight_right" ]
property var bodyParts: [ "body", "door_left", "door_right",
"trunk", "hood" ]
property var transparentGlassParts: [ "d_glass" ]
property var tireParts: [ "tire_front_left", "tire_front_right",
"tire_rear_left", "tire_rear_right" ]
property var interiorParts: [ "interior" ]
onStatusChanged: {
if (status === SceneLoader.Ready) {
sceneHelper.addBasicMaterials(modelLoader, bodyMaterial, bodyParts)
sceneHelper.addBasicMaterials(modelLoader, transparentGlassMaterial,
transparentGlassParts)
sceneHelper.addBasicMaterials(modelLoader, interiorMaterial, interiorParts)
sceneHelper.addTextureMaterial(modelLoader, lampsMaterial, lampParts[0])
sceneHelper.addTextureMaterial(modelLoader, lampsMaterial, lampParts[1])
sceneHelper.addTextureMaterial(modelLoader, lampsMaterial, lampParts[2])
sceneHelper.addTextureMaterial(modelLoader, lampsMaterial, lampParts[3])
sceneHelper.addTextureMaterial(modelLoader, lampsMaterial, lampParts[4])
sceneHelper.addTextureMaterial(modelLoader, lampsMaterial, lampParts[5])
sceneHelper.addTextureMaterial(modelLoader, tireMaterial, tireParts[0])
sceneHelper.addTextureMaterial(modelLoader, tireMaterial, tireParts[1])
sceneHelper.addTextureMaterial(modelLoader, tireMaterial, tireParts[2])
sceneHelper.addTextureMaterial(modelLoader, tireMaterial, tireParts[3])
floorPlane.enabled = true
}
}
}
components : [carTransform, modelLoader]
}
Entity {
id: floorPlane
enabled: false
DiffuseMapMaterial {
id: planeMaterial
ambient: Qt.rgba(0, 0, 0, 1)
specular: Qt.rgba(0, 0, 0, 1)
diffuse: TextureLoader { source: "qrc:/shade.png" }
}
Transform {
id: planeRotation
matrix: {
var m = Qt.matrix4x4()
m.rotate(carRotation, Qt.vector3d(0, 1, 0))
m.scale(1.35)
return m
}
}
PlaneMesh {
id: planeMesh
width: 70
height: 70
}
components : [planeMesh, planeMaterial, planeRotation]
}
function highlightItem(idx) {
carRotationAnimation.stop()
carResetRotationAnimation.start()
highlighting = true
var highlightComponent
var highlightMaterial
var originalMaterial
switch (idx) {
case 1:
highlightComponent = "tire_front_left"
highlightMaterial = tireMaterialHighlight
originalMaterial = tireMaterial
lightPos = positionLeftFront.times(lightPosMultiplier)
cameraPos = positionLeftFront
break
case 2:
highlightComponent = "tire_front_right"
highlightMaterial = tireMaterialHighlight
originalMaterial = tireMaterial
lightPos = positionRightFront.times(lightPosMultiplier)
cameraPos = positionRightFront
break
case 3:
highlightComponent = "tire_rear_right"
highlightMaterial = tireMaterialHighlight
originalMaterial = tireMaterial
lightPos = positionRightRear.times(lightPosMultiplier)
cameraPos = positionRightRear
break
case 4:
highlightComponent = "tire_rear_left"
highlightMaterial = tireMaterialHighlight
originalMaterial = tireMaterial
lightPos = positionLeftRear.times(lightPosMultiplier)
cameraPos = positionLeftRear
break
case 5:
highlightComponent = "headlight_left"
highlightMaterial = bodyMaterialHighlight
originalMaterial = lampsMaterial
lightPos = positionFrontLeftHigh.times(lightPosMultiplier)
cameraPos = positionFrontLeftHigh
break
case 6:
highlightComponent = "headlight_right"
highlightMaterial = bodyMaterialHighlight
originalMaterial = lampsMaterial
lightPos = positionFrontRightHigh.times(lightPosMultiplier)
cameraPos = positionFrontRightHigh
break
case 7:
highlightComponent = "daylight_right"
highlightMaterial = bodyMaterialHighlight
originalMaterial = lampsMaterial
lightPos = positionFrontRightLow.times(lightPosMultiplier)
cameraPos = positionFrontRightLow
break
case 8:
highlightComponent = "daylight_left"
highlightMaterial = bodyMaterialHighlight
originalMaterial = lampsMaterial
lightPos = positionFrontLeftLow.times(lightPosMultiplier)
cameraPos = positionFrontLeftLow
break
case 9:
highlightComponent = "taillight_left"
highlightMaterial = bodyMaterialHighlight
originalMaterial = lampsMaterial
lightPos = positionRearLeft.times(lightPosMultiplier)
cameraPos = positionRearLeft
break
case 10:
highlightComponent = "taillight_right"
highlightMaterial = bodyMaterialHighlight
originalMaterial = lampsMaterial
lightPos = positionRearRight.times(lightPosMultiplier)
cameraPos = positionRearRight
break
default:
lightPos = defaultLightPosition
cameraPos = defaultCameraPosition
}
if (previousComponent !== undefined)
sceneHelper.replaceMaterial(modelLoader, previousComponent, previousMaterial)
if (highlightComponent !== undefined)
sceneHelper.replaceMaterial(modelLoader, highlightComponent, highlightMaterial)
previousComponent = highlightComponent
previousMaterial = originalMaterial
}
function highlightOpenDoors(openDoors) {
carRotationAnimation.stop()
carResetRotationAnimation.start()
highlighting = true
var openLeft = false
var openRight = false
var openBack = false
var openFront = false
// Check with bitwise operators, as they can be open in any combination
if (openDoors & door_left) {
sceneHelper.replaceMaterial(modelLoader, "door_left", bodyMaterialHighlight)
openLeft = true
} else {
sceneHelper.replaceMaterial(modelLoader, "door_left", bodyMaterial)
}
if (openDoors & door_right) {
sceneHelper.replaceMaterial(modelLoader, "door_right", bodyMaterialHighlight)
openRight = true
} else {
sceneHelper.replaceMaterial(modelLoader, "door_right", bodyMaterial)
}
if (openDoors & door_trunk) {
sceneHelper.replaceMaterial(modelLoader, "trunk", bodyMaterialHighlight)
openBack = true
} else {
sceneHelper.replaceMaterial(modelLoader, "trunk", bodyMaterial)
}
if (openDoors & door_hood) {
openFront = true
sceneHelper.replaceMaterial(modelLoader, "hood", bodyMaterialHighlight)
} else {
sceneHelper.replaceMaterial(modelLoader, "hood", bodyMaterial)
}
if (openRight && openLeft || openBack && openFront) {
lightPos = positionTop.times(0.5)
cameraPos = positionTop
} else if (openRight) {
lightPos = positionRight.times(0.33)
lightPos.y += 15.0
cameraPos = positionRight
} else if (openLeft) {
lightPos = positionLeft.times(0.33)
lightPos.y += 15.0
cameraPos = positionLeft
} else if (openBack) {
lightPos = positionBack.times(0.75)
cameraPos = positionBack
} else if (openFront) {
lightPos = positionFront.times(1.0)
cameraPos = positionFront
} else {
lightPos = defaultLightPosition
cameraPos = defaultCameraPosition
}
}
onCameraPosChanged: {
if (!highlighting)
return
// Update both camera and light positions
cameraAnimation.to = cameraPos
lightAnimation.to = lightPos
cameraAnimation.restart()
lightAnimation.restart()
highlighting = false
}
Quick.PropertyAnimation {
running: false
id: cameraAnimation
target: camera
property: "position"
duration: 1000
easing.type: Easing.InOutQuad
}
Quick.PropertyAnimation {
running: false
id: lightAnimation
target: carModelEntity
property: "lightPosition"
duration: 1000
easing.type: Easing.Linear
}
Quick.RotationAnimation on carRotation {
id: carRotationAnimation
running: false
from: 0.0
to: 360.0
duration: 15000
loops: -1
}
Quick.RotationAnimation on carRotation {
id: carResetRotationAnimation
running: false
to: (carRotation > 180.0) ? 360.0 : 0.0 // TODO: Try to make animation "natural". Still works weirdly sometimes.
duration: 1000
loops: -1
easing.type: Easing.InOutQuad
}
function resetHighlight() {
if (doorAction)
highlightOpenDoors(0)
else
highlightItem(defaultHighlight)
doorAction = false
}
function highlightLamp() {
highlightType = Math.floor(Math.random() * 6) + 5
highlightItem(highlightType)
return highlightType
}
function highlightTire() {
highlightType = Math.floor(Math.random() * 4) + 1
highlightItem(highlightType)
return highlightType
}
function toggleIdleTimer(isVisible) {
if (isVisible) {
idleTimer.restart()
} else {
carRotationAnimation.stop()
carRotation = 0.0
idleTimer.stop()
}
}
Quick.Timer {
id: idleTimer
interval: 10000
onTriggered: {
carRotationAnimation.restart()
}
}
}
这是一个基于Qt 3D的跑车仪表盘项目,具有以下亮点:
项目概述
这是一个使用Qt 3D技术实现的跑车仪表盘应用程序。项目采用QML作为界面描述语言,结合C++后端逻辑,实现了3D渲染的仪表盘界面。基于Qt3D的跑车3D模型展示系统,主要实现以下功能:1) 采用QML和Qt3D构建3D场景,支持模型加载、材质控制和灯光设置;2) 实现车辆部件高亮显示功能,可定位轮胎、车灯等不同部位;3) 提供车门开合状态展示,支持多角度观察;4) 包含预设相机和灯光位置,实现自动视角切换;5) 支持车辆旋转动画和复位功能。系统通过SceneLoader加载3D模型,使用PhongMaterial等材质渲染不同部件,并利用PropertyAnimation实现平滑过渡效果。该方案适用于汽车展示、虚拟装配等场景。
技术亮点
- **3D场景管理**
-
通过`SceneHelper`类实现了完整的3D场景管理功能
-
支持实体查找、材质替换、相机控制等核心3D操作
-
提供了灵活的场景加载和组件管理机制
- **材质系统**
-
实现了多种材质处理方式:
-
基础材质添加 (`addBasicMaterials`)
-
纹理材质支持 (`addTextureMaterial`)
-
材质动态替换 (`replaceMaterial`)
-
支持对3D模型进行精细的材质控制
- **资源管理**
-
完善的图标资源系统,包含:
-
左侧图标 (`icons-left`)
-
右侧图标 (`icons-right`)
-
道路图标 (`Road`)
-
选中状态图标
-
资源分类清晰,便于维护和扩展
- **架构设计**
-
采用QML/C++混合架构
-
通过`qmlRegisterType`注册C++类到QML环境
-
实现了良好的前后端分离
应用价值
-
**专业性强**:专门针对跑车仪表盘场景设计,具有很强的专业性
-
**扩展性好**:模块化的设计使得功能扩展和维护变得容易
-
**性能优化**:使用Qt 3D原生渲染,确保了良好的性能表现
-
**实用性高**:可直接用于车载系统或模拟器开发
这个项目展示了Qt 3D在专业图形界面开发中的强大能力,特别是在车载显示系统等领域的应用潜力。