场景实例化与变换
同一个 glTF 模型可以在场景里摆多份。Handle<WorldAsset> 是一张提货单,克隆它不复制资产本体——多份实例共享同一份网格和材质数据,只各自维护自己的变换。
同一模型摆多份
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(2.0, 1.5, 3.0).looking_at(Vec3::ZERO, 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),
));
let helmet =
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
for i in 0..3 {
let x = (i as f32 - 1.0) * 1.5;
commands.spawn((
WorldAssetRoot(helmet.clone()),
Transform::from_xyz(x, 0.0, 0.0),
));
}
}Listing 23-4:同一个头盔摆三份——克隆 Handle 不复制资产(examples/listing-23-04.rs)
console
cargo run -p ch23-gltf --example listing-23-04三只头盔一字排开。它们的网格数据在 GPU 上只有一份,渲染时用不同的变换矩阵画三次——这就是实例化的开销优势。
给实例加变换
WorldAssetRoot 实体上的 Transform 会影响整个 glTF 场景的所有子实体。缩放、旋转、位移,和第 12 章的手艺完全一样:
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(3.0, 2.0, 4.0).looking_at(Vec3::ZERO, 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),
));
let helmet =
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
commands.spawn((
WorldAssetRoot(helmet.clone()),
Transform::from_xyz(-1.5, 0.0, 0.0).with_scale(Vec3::splat(0.8)),
));
commands.spawn((
WorldAssetRoot(helmet.clone()),
Transform::from_xyz(0.0, 0.0, 0.0),
));
commands.spawn((
WorldAssetRoot(helmet),
Transform::from_xyz(1.5, 0.0, 0.0)
.with_scale(Vec3::splat(1.2))
.with_rotation(Quat::from_rotation_y(0.5)),
));
}Listing 23-5:三只头盔——缩小、原大、放大并旋转(examples/listing-23-05.rs)
console
cargo run -p ch23-gltf --example listing-23-05注意 Transform 是挂在 WorldAssetRoot 实体上的,glTF 场景展开后的子实体各自还有自己的局部变换(来自 glTF 节点)。最终的渲染变换是层级传播的结果——第 12 章的 GlobalTransform 规矩原样适用。
为什么 clone() 是安全的
Handle::clone() 只增加引用计数,不复制资产数据。这和第 14 章讲的 Handle 机制完全一致:同一个 Handle<WorldAsset> 的多个克隆指向 Assets<Scene> 里的同一份数据。销毁一个实例(despawn)只减少引用计数,资产在最后一个 Handle 销毁前不会被回收。