qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记
文章目录
- [qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记](#qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记)
1.例程运行效果
运行该项目需要自己准备一个模型文件
2.例程缩略图
3.项目文件列表
runtimeloader/
├── CMakeLists.txt
├── main.cpp
├── main.qml
├── qml.qrc
└── runtimeloader.pro
1 directory, 5 files
4.main.qml
js
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.platform
import QtCore
import QtQuick3D
import QtQuick3D.Helpers
import QtQuick3D.AssetUtils
// 创建一个窗口根节点,设置窗口大小和显示状态
Window {
id: windowRoot
visible: true
width: 1280
height: 720
property url importUrl; // 用于导入模型的URL
//! [base scene] 基础场景
View3D {
id: view3D
anchors.fill: parent // 填充父窗口
environment: SceneEnvironment {
id: env
backgroundMode: SceneEnvironment.SkyBox // 背景模式为天空盒
lightProbe: Texture {
textureData: ProceduralSkyTextureData{} // 使用程序化天空纹理
}
InfiniteGrid {
visible: helper.gridEnabled // 是否显示网格
gridInterval: helper.gridInterval // 网格间隔
}
}
camera: helper.orbitControllerEnabled ? orbitCamera : wasdCamera // 根据控制器选择相机
// 设置方向光源
DirectionalLight {
eulerRotation.x: -35
eulerRotation.y: -90
castsShadow: true // 启用阴影
}
Node {
id: orbitCameraNode
PerspectiveCamera {
id: orbitCamera // 轨道相机
}
}
// 第一人称相机(WASD控制)
PerspectiveCamera {
id: wasdCamera
onPositionChanged: {
// 更新相机的近远裁剪面
let distance = position.length()
if (distance < 1) {
clipNear = 0.01
clipFar = 100
} else if (distance < 100) {
clipNear = 0.1
clipFar = 1000
} else {
clipNear = 1
clipFar = 10000
}
}
}
//! [base scene]
// 重置视图的函数
function resetView() {
if (importNode.status === RuntimeLoader.Success) {
helper.resetController()
}
}
//! [instancing] 实例化
RandomInstancing {
id: instancing
instanceCount: 30 // 设置实例数量
position: InstanceRange {
property alias boundsDiameter: helper.boundsDiameter
from: Qt.vector3d(-3*boundsDiameter, -3*boundsDiameter, -3*boundsDiameter); // 位置范围
to: Qt.vector3d(3*boundsDiameter, 3*boundsDiameter, 3*boundsDiameter)
}
color: InstanceRange { from: "black"; to: "white" } // 颜色范围
}
//! [instancing]
QtObject {
id: helper
property real boundsDiameter: 0 // 场景边界的直径
property vector3d boundsCenter // 场景中心
property vector3d boundsSize // 场景大小
property bool orbitControllerEnabled: true // 是否启用轨道控制器
property bool gridEnabled: gridButton.checked // 是否启用网格
property real cameraDistance: orbitControllerEnabled ? orbitCamera.z : wasdCamera.position.length() // 相机与中心的距离
property real gridInterval: Math.pow(10, Math.round(Math.log10(cameraDistance)) - 1) // 网格间隔计算
// 更新场景边界信息
function updateBounds(bounds) {
boundsSize = Qt.vector3d(bounds.maximum.x - bounds.minimum.x,
bounds.maximum.y - bounds.minimum.y,
bounds.maximum.z - bounds.minimum.z)
boundsDiameter = Math.max(boundsSize.x, boundsSize.y, boundsSize.z)
boundsCenter = Qt.vector3d((bounds.maximum.x + bounds.minimum.x) / 2,
(bounds.maximum.y + bounds.minimum.y) / 2,
(bounds.maximum.z + bounds.minimum.z) / 2 )
wasdController.speed = boundsDiameter / 1000.0 // 更新控制器速度
wasdController.shiftSpeed = 3 * wasdController.speed
wasdCamera.clipNear = boundsDiameter / 100
wasdCamera.clipFar = boundsDiameter * 10
view3D.resetView() // 重置视图
}
// 重置控制器
function resetController() {
orbitCameraNode.eulerRotation = Qt.vector3d(0, 0, 0)
orbitCameraNode.position = boundsCenter
orbitCamera.position = Qt.vector3d(0, 0, 2 * helper.boundsDiameter)
orbitCamera.eulerRotation = Qt.vector3d(0, 0, 0)
orbitControllerEnabled = true
}
// 切换控制器
function switchController(useOrbitController) {
if (useOrbitController) {
let wasdOffset = wasdCamera.position.minus(boundsCenter)
let wasdDistance = wasdOffset.length()
let wasdDistanceInPlane = Qt.vector3d(wasdOffset.x, 0, wasdOffset.z).length()
let yAngle = Math.atan2(wasdOffset.x, wasdOffset.z) * 180 / Math.PI
let xAngle = -Math.atan2(wasdOffset.y, wasdDistanceInPlane) * 180 / Math.PI
orbitCameraNode.position = boundsCenter
orbitCameraNode.eulerRotation = Qt.vector3d(xAngle, yAngle, 0)
orbitCamera.position = Qt.vector3d(0, 0, wasdDistance)
orbitCamera.eulerRotation = Qt.vector3d(0, 0, 0)
} else {
wasdCamera.position = orbitCamera.scenePosition
wasdCamera.rotation = orbitCamera.sceneRotation
wasdController.focus = true
}
orbitControllerEnabled = useOrbitController
}
}
//! [runtimeloader] 运行时加载器
RuntimeLoader {
id: importNode
source: windowRoot.importUrl // 导入模型的URL
instancing: instancingButton.checked ? instancing : null // 实例化开关
onBoundsChanged: helper.updateBounds(bounds) // 更新场景边界
}
//! [runtimeloader]
//! [bounds] 场景边界
Model {
parent: importNode
source: "#Cube" // 默认使用立方体模型
materials: PrincipledMaterial {
baseColor: "red" // 设置基础颜色为红色
}
opacity: 0.2 // 设置模型透明度
visible: visualizeButton.checked && importNode.status === RuntimeLoader.Success // 根据条件显示模型
position: helper.boundsCenter
scale: Qt.vector3d(helper.boundsSize.x / 100,
helper.boundsSize.y / 100,
helper.boundsSize.z / 100)
}
//! [bounds]
//! [status report] 状态报告
Rectangle {
id: messageBox
visible: importNode.status !== RuntimeLoader.Success // 如果导入失败,显示错误消息
color: "red"
width: parent.width * 0.8
height: parent.height * 0.8
anchors.centerIn: parent
radius: Math.min(width, height) / 10
opacity: 0.6
Text {
anchors.fill: parent
font.pixelSize: 36
text: "Status: " + importNode.errorString + "\nPress \"Import...\" to import a model" // 显示错误信息
color: "white"
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
//! [status report]
}
//! [camera control] 相机控制
OrbitCameraController {
id: orbitController
origin: orbitCameraNode
camera: orbitCamera
enabled: helper.orbitControllerEnabled // 根据状态启用或禁用轨道控制器
}
WasdController {
id: wasdController
controlledObject: wasdCamera
enabled: !helper.orbitControllerEnabled // 根据状态启用或禁用WASD控制器
}
//! [camera control]
// 界面控制面板
Pane {
width: parent.width
contentHeight: controlsLayout.implicitHeight
RowLayout {
id: controlsLayout
Button {
id: importButton
text: "Import..."
onClicked: fileDialog.open() // 打开文件对话框
focusPolicy: Qt.NoFocus
}
Button {
id: resetButton
text: "Reset view"
onClicked: view3D.resetView() // 重置视图
focusPolicy: Qt.NoFocus
}
Button {
id: visualizeButton
checkable: true
text: "Visualize bounds" // 显示场景边界
focusPolicy: Qt.NoFocus
}
Button {
id: instancingButton
checkable: true
text: "Instancing" // 开启实例化
focusPolicy: Qt.NoFocus
}
Button {
id: gridButton
text: "Show grid" // 显示网格
focusPolicy: Qt.NoFocus
checkable: true
checked: false
}
Button {
id: controllerButton
text: helper.orbitControllerEnabled ? "Orbit" : "WASD" // 切换控制器
onClicked: helper.switchController(!helper.orbitControllerEnabled)
focusPolicy: Qt.NoFocus
}
RowLayout {
Label {
text: "Material Override"
}
ComboBox {
id: materialOverrideComboBox
textRole: "text"
valueRole: "value"
implicitContentWidthPolicy: ComboBox.WidestText
onActivated: env.debugSettings.materialOverride = currentValue // 选择材质覆盖
model: [
{ value: DebugSettings.None, text: "None"},
{ value: DebugSettings.BaseColor, text: "Base Color"},
{ value: DebugSettings.Roughness, text: "Roughness"},
{ value: DebugSettings.Metalness, text: "Metalness"},
{ value: DebugSettings.Diffuse, text: "Diffuse"},
{ value: DebugSettings.Specular, text: "Specular"},
{ value: DebugSettings.ShadowOcclusion, text: "Shadow Occlusion"},
{ value: DebugSettings.Emission, text: "Emission"},
{ value: DebugSettings.AmbientOcclusion, text: "Ambient Occlusion"},
{ value: DebugSettings.Normals, text: "Normals"},
{ value: DebugSettings.Tangents, text: "Tangents"},
{ value: DebugSettings.Binormals, text: "Binormals"},
{ value: DebugSettings.F0, text: "F0"}
]
}
}
CheckBox {
text: "Wireframe" // 启用或禁用线框模式
checked: env.debugSettings.wireframeEnabled
onCheckedChanged: {
env.debugSettings.wireframeEnabled = checked
}
}
}
}
// 文件对话框,允许用户选择glTF模型文件
FileDialog {
id: fileDialog
nameFilters: ["glTF files (*.gltf *.glb)", "All files (*)"]
onAccepted: importUrl = file // 选择文件后导入
Settings {
id: fileDialogSettings
category: "QtQuick3D.Examples.RuntimeLoader"
property alias folder: fileDialog.folder
}
}
// 调试视图切换按钮
Item {
width: debugViewToggleText.implicitWidth
height: debugViewToggleText.implicitHeight
anchors.right: parent.right
Label {
id: debugViewToggleText
text: "Click here " + (dbg.visible ? "to hide DebugView" : "for DebugView")
anchors.right: parent.right
anchors.top: parent.top
}
MouseArea {
anchors.fill: parent
onClicked: dbg.visible = !dbg.visible // 切换调试视图可见性
DebugView {
y: debugViewToggleText.height * 2
anchors.right: parent.right
source: view3D
id: dbg
visible: false
}
}
}
}
5.main.cpp
cpp
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifdef HAS_MODULE_QT_WIDGETS
# include <QApplication>
#endif
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QtQuick3D/qquick3d.h>
int main(int argc, char *argv[])
{
#ifdef HAS_MODULE_QT_WIDGETS
QApplication app(argc, argv);
#else
QGuiApplication app(argc, argv);
#endif
app.setOrganizationName("The Qt Company");
app.setOrganizationDomain("qt.io");
app.setApplicationName("Runtime Asset Loading Example");
const auto importUrl = argc > 1 ? QUrl::fromLocalFile(argv[1]) : QUrl{};
if (importUrl.isValid())
qDebug() << "Importing" << importUrl;
QSurfaceFormat::setDefaultFormat(QQuick3D::idealSurfaceFormat(4));
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
if (engine.rootObjects().isEmpty()) {
qWarning() << "Could not find root object in" << url;
return -1;
}
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
if (window)
window->setProperty("importUrl", importUrl);
return app.exec();
}
6.CMakeLists.txt
cmake
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(runtimeloader LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Quick3D Widgets)
qt_add_executable(runtimeloader
main.cpp
)
set_target_properties(runtimeloader PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(runtimeloader PUBLIC
Qt::Core
Qt::Gui
Qt::Quick
Qt::Quick3D
)
if(TARGET Qt::Widgets)
target_compile_definitions(runtimeloader PUBLIC
HAS_MODULE_QT_WIDGETS
)
target_link_libraries(runtimeloader PUBLIC
Qt::Widgets
)
endif()
qt_add_qml_module(runtimeloader
URI Example
VERSION 1.0
QML_FILES main.qml
NO_RESOURCE_TARGET_PATH
)
install(TARGETS runtimeloader
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
qt_generate_deploy_qml_app_script(
TARGET runtimeloader
OUTPUT_SCRIPT deploy_script
MACOS_BUNDLE_POST_BUILD
NO_UNSUPPORTED_PLATFORM_ERROR
DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
install(SCRIPT ${deploy_script})