Skip to content

加载第一个 glTF

bevy 的 glTF 加载器随 DefaultPlugins 自动注册。加载一个 .gltf.glb 文件只需要两步:AssetServer::load 取句柄,WorldAssetRoot 组件把场景挂上实体。

rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
    ));

    commands.spawn((
        DirectionalLight {
            contact_shadows_enabled: true,
            ..default()
        },
        Transform::from_xyz(4.0, 25.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    commands.spawn(WorldAssetRoot(
        asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
    ));
}

Listing 23-1:最小 glTF 加载——三行核心代码(examples/listing-23-01.rs)

console
cargo run -p ch23-gltf --example listing-23-01

一个头盔出现在窗口里。

GltfAssetLabel::Scene(0) 是什么意思

glTF 文件内部可以包含多个场景(Scene)。GltfAssetLabel::Scene(0) 表示"取第一个场景"。.from_asset("path") 把标签拼到路径后面,等价于 "path#Scene0"

为什么必须写标签?因为 AssetServer::load 不知道你要取 glTF 里的哪一部分——是整个场景、某个节点、还是单独一个网格。标签就是精确的取货地址。不写标签,加载器不知道该给你什么,行为未定义。

WorldAssetRoot 组件

WorldAssetRoot(Handle<WorldAsset>)bevy_scene 提供的组件。挂上它,Bevy 的场景系统会在下一帧把 glTF 场景里的全部实体——网格、材质、变换层级——自动展开为 ECS 实体,作为该实体的子节点。

glTF 场景里的变换层级原样保留:如果模型在 Blender 里有父子关系,加载到 Bevy 后父子关系不变。

机位与灯

3D 场景的标配——Camera3dDirectionalLight——在 Listing 23-1 里照旧需要手动 spawn。glTF 文件可能自带灯光信息(通过 KHR_lights_punctual 扩展),但 Bevy 默认不导入它们,你需要自己在场景里布置光照。