AnvilKitAnvilKit

资源管理

资源管理 — glTF 加载、动画、程序化网格

anvilkit-assets 提供资产加载与管理功能,包括 glTF 模型、骨骼动画和程序化网格生成。

glTF 加载

use anvilkit_assets::gltf_loader::GltfLoader;

let scene = GltfLoader::load("assets/models/character.glb")?;

for mesh in &scene.meshes {
    println!("Mesh: {} vertices, {} triangles",
        mesh.positions.len(),
        mesh.indices.len() / 3
    );
}

骨骼动画

支持三种插值模式:

模式说明
Linear线性插值(平移/缩放使用 lerp,旋转使用 slerp)
Step阶跃函数,无过渡
CubicSpline三次样条插值,曲线更平滑
use anvilkit_assets::animation::*;

let mut player = AnimationPlayer::new();
player.play(animation_clip, true); // loop = true
player.update(dt);
let joint_transforms = player.joint_transforms();

程序化网格生成

MeshData 提供常用几何体的工厂方法:

use anvilkit_assets::mesh::MeshData;

let cube = MeshData::generate_box(1.0);       // 24 顶点, 36 索引
let plane = MeshData::generate_plane(10.0);    // 4 顶点, 6 索引(XZ 平面, +Y 法线)
let sphere = MeshData::generate_sphere(1.0, 32, 16); // UV 球体

// 转换为 GPU 上传格式
let pbr_vertices = cube.to_pbr_vertices();
方法参数说明
generate_box(size)f32居中立方体,6 面独立法线
generate_plane(size)f32原点居中 XZ 平面,+Y 法线
generate_sphere(radius, segments, rings)f32, u32, u32UV 球体
to_pbr_vertices()转换为交错 InterleavedPbrVertex(48 字节/顶点)

StandardMaterial

高级 PBR 渲染使用 StandardMaterial(位于 anvilkit-render):

use anvilkit_render::renderer::standard_material::StandardMaterial;

commands.spawn((
    mesh_handle,
    StandardMaterial::new()
        .with_base_color([1.0, 0.2, 0.1, 1.0])
        .with_metallic(0.0)
        .with_roughness(0.7),
    Transform::default(),
));

render_extract_system 自动从 (MeshHandle, StandardMaterial, GlobalTransform) 查询生成 DrawCommand

材质

use anvilkit_assets::material::Material;

let material = Material {
    base_color: [1.0, 0.8, 0.6, 1.0],
    metallic: 0.0,
    roughness: 0.7,
    albedo_texture: Some(texture_handle),
    normal_texture: None,
    metallic_roughness_texture: None,
};

资产服务器

支持同步和异步加载:

use anvilkit_assets::asset_server::AssetServer;

let mut server = AssetServer::new("assets/");

// 同步:注册加载意图(无 I/O)
let handle = server.load::<Mesh>("models/tree.glb");

// 异步:在后台线程中加载文件(固定线程池,1-4 worker)
let async_handle = server.load_async::<Vec<u8>>("textures/atlas.png");

// 在游戏循环中:轮询已完成的加载
server.process_completed();
for result in server.drain_completed() {
    match result.data {
        Ok(bytes) => { /* 解析并使用加载的数据 */ },
        Err(err) => { /* 处理错误 */ },
    }
}

DependencyGraph

DependencyGraph 使用类型安全的 AssetId 句柄(而非裸 u64)追踪资产之间的父子关系。它集成在 AssetServer 中,驱动级联卸载。

方法签名说明
add_dependency(&mut self, parent: AssetId, child: AssetId)记录 parent 依赖于 child
remove_asset(&mut self, id: AssetId) -> Vec<AssetId>移除资产并返回孤立的子资产(没有剩余父资产的子资产)。
dependents(&self, id: AssetId) -> &[AssetId]列出所有依赖于给定资产的资产。
dependencies(&self, id: AssetId) -> &[AssetId]列出给定资产依赖的所有资产。
use anvilkit_assets::dependency::DependencyGraph;

let mut graph = DependencyGraph::new();
graph.add_dependency(scene_id, mesh_id);
graph.add_dependency(scene_id, texture_id);

// Query
let deps = graph.dependencies(scene_id); // [mesh_id, texture_id]
let parents = graph.dependents(mesh_id);  // [scene_id]

AssetCache(内容哈希缓存)

AssetServer 包含基于内容哈希的缓存,用于去重加载:

  • 当资产在 process_completed() 中完成加载时,计算其内容哈希并存储在缓存中。
  • 后续的 load_async 调用在缓存命中时完全跳过磁盘读取,立即返回缓存数据。
  • 当资产被卸载时,缓存会自动清理。

这对游戏代码是透明的 — 无需修改任何 API 即可享受缓存带来的性能提升。

级联卸载

AssetServer::unload(id) 执行完整的依赖感知清理:

  1. DependencyGraph 上调用 remove_and_cascade 查找孤立的子资产。
  2. 清理被卸载资产的资产缓存、解析存储和状态追踪。
  3. 递归卸载没有其他父资产的子资产。
// Unload a scene — its exclusive meshes and textures are cleaned up automatically
server.unload(scene_id);

类型化解析存储

AssetServer 提供类型化存储,用于存放解析后的资产值,将原始字节加载与应用层类型桥接:

方法签名说明
insert_parsed<T: 'static>(&mut self, id: AssetId, value: T)存储任意类型的解析后资产值。
get_parsed<T: 'static>(&self, id: AssetId) -> Option<&T>获取之前存储的解析后值。
// After loading raw bytes, parse and store the result
let mesh: Mesh = parse_mesh(&raw_bytes);
server.insert_parsed::<Mesh>(mesh_id, mesh);

// Later, retrieve the parsed mesh
if let Some(mesh) = server.get_parsed::<Mesh>(mesh_id) {
    // Use the mesh
}

热重载

文件监视功能,用于开发期间自动刷新资产(需要 hot-reload feature)。变更事件经过 200ms 防抖处理,同时支持 Remove 事件。

use anvilkit_assets::hot_reload::FileWatcher;

let mut watcher = FileWatcher::new("assets").unwrap();

// 在游戏循环中:
for path in watcher.poll_changes() {
    if FileWatcher::is_shader(&path) {
        // 使用新着色器重建渲染管线
    } else if FileWatcher::is_texture(&path) {
        // 将纹理重新上传到 GPU
    }
}

目录