46 个重大发布
使用旧的 Rust 2015
0.47.0 | 2023 年 11 月 14 日 |
---|---|
0.45.0 | 2022 年 11 月 18 日 |
0.42.0 | 2022 年 2 月 23 日 |
0.41.0 | 2021 年 11 月 28 日 |
0.1.1 | 2015 年 7 月 17 日 |
#317 in 游戏开发
140 每月下载量
87KB
1.5K SLoC
skeletal_animation
一个用于数据驱动的骨骼动画的 Rust 库。
概述
此库允许您在 JSON 中定义动画剪辑、状态机和混合树,以便在运行时加载和重新加载,而无需重新编译您的 Rust 项目。
用法
资产定义文件
动画资产,目前包括 AnimationClips、DifferenceClips 和 AnimationControllers,在定义的 JSON 文件中声明,格式如下
{
"animation_clips": [],
"difference_clips": [],
"animation_controllers": []
}
在运行时,可以通过以下方式通过 AssetManager
从一个或多个定义文件中加载资产
let mut asset_manager = AssetManager::<QVTransform>::new(); // To use QVTransforms (Quaternions for rotations, Vector3s for translations)
let mut asset_manager = AssetManager::<DualQuaternion<f32>>::new(); // To use DualQuaternions
asset_manager.load_animations("assets/animation_assets.json");
asset_manager.load_animations("assets/more_animation_assets.json");
动画剪辑
动画剪辑声明如下
{
"animation_clips": [{
"name": "walk-forward",
"source": "assets/walk.dae",
}, {
"name": "run-forward",
"source": "assets/run.dae",
}]
}
差异剪辑
差异剪辑是通过两个动画剪辑之间的 差异 定义的动画剪辑。它们打算用于加性混合节点,其中加性剪辑(例如,头部向左转)被添加到另一个节点(例如,行走动画)的输出中。
{
"difference_clips": [{
"name": "head-look-left-additive",
"source_clip": "head-look-left",
"reference_clip": "reference-pose"
}]
}
其中
name
应该是动画剪辑的唯一标识符,可以从动画控制器定义中引用。source_clip
是包含所需动画的 COLLADA 文件的路径,例如一个左侧转向的 "T-Pose" 角色头reference_clip
是包含所需参考动画的 COLLADA 文件的路径,例如一个 "T-Pose" 角色头
动画控制器
动画控制器是状态机,它由以下内容组成
- 将在此控制器中引用的参数列表,这些参数将由状态转换条件和混合树节点引用。
- 状态列表,其中每个状态包括
- 用于识别状态的唯一名称。
- 一个混合树,它根据某些参数值混合一个或多个动画剪辑。
- 同一控制器内其他状态的转换列表,其中每个转换都有
- 目标状态名称。
- 基于某些参数值的条件。
- 转换持续时间。
- 控制器应启动的初始状态名称。
一个示例控制器定义
{
"animation_controllers": [{
"name": "human-controller",
"parameters": [
"forward-speed",
"forward-to-strafe",
"walk-to-run",
"left-to-right"
],
"states": [ {
"name": "walking-forward",
"blend_tree": {
"type": "LerpNode",
"param": "forward-to-strafe",
"inputs": [ {
"type": "LerpNode",
"param": "walk-to-run",
"inputs": [{
"type": "ClipNode",
"clip_source": "walk-forward"
}, {
"type": "ClipNode",
"clip_source": "run-forward"
}]
}, {
"type": "LerpNode",
"param": "left-to-right",
"inputs": [{
"type": "ClipNode",
"clip_source": "walk-left",
}, {
"type": "ClipNode",
"clip_source": "walk-right"
}]
}]
},
"transitions": [ {
"target_state": "stand-idle",
"duration": 0.5,
"condition": {
"parameter": "forward-speed",
"operator": "<",
"value": 0.1
}
}]
}, {
"name": "stand-idle",
"blend_tree": {
"type": "ClipNode",
"clip_source": "stand-idle"
},
"transitions": [ {
"target_state": "walking-forward",
"duration": 0.5,
"condition": {
"parameter": "forward-speed",
"operator": ">",
"value": 0.1
}
} ]
} ],
"initial_state": "stand-idle"
}]
}
在运行时,在加载到AssetManger之后,可以使用以下方式初始化AnimationController
:
// First, need to load the shared skeleton object(eg from a COLLADA document)
// This will become more elegant, i promise :)
let skeleton = {
let collada_document = ColladaDocument::from_path(&Path::new("assets/suit_guy.dae")).unwrap();
let skeleton_set = collada_document.get_skeletons().unwrap();
Rc::new(Skeleton::from_collada(&skeleton_set[0]))
}
// Create the AnimationController from the definition, the skeleton, and the clips previously loaded
// by the animation manager
let controller_def = asset_manager.controller_defs["human-controller"].clone();
let controller = AnimationController::new(controller_def, skeleton.clone(), &asset_manager.animation_clips);
目前,skeletal_animation
假设使用Piston风格的循环事件,其中我们分别有带有delta-time的update
(更新事件)和带有自上次更新以来外推delta-time的render
(渲染事件),因此在每个游戏循环中的update
事件中我们需要:
// Set any relevant parameters on the controller:
controller.set_param_value("forward-speed", 1.8);
// Update the controller's local clock
controller.update(delta_time);
然后,在render
中,我们可以通过以下方式获取当前骨骼姿态,以矩阵或双四元数的形式表示:
// Matrices:
let mut global_poses: [Matrix4<f32>; 64] = [ mat4_id(); 64 ];
controller.get_output_pose(args.ext_dt, &mut global_poses[0 .. skeleton.joints.len()]);
// DualQuaternions:
let mut global_poses: [DualQuaternion<f32>; 64] = [ dual_quaternion::id(); 64 ];
controller.get_output_pose(args.ext_dt, &mut global_poses[0 .. skeleton.joints.len()]);
其中args.ext_dt
是自上次更新以来的外推时间。要实际使用骨骼姿态渲染内容,您可以:
- 使用gfx_debug_draw绘制带姿态的骨骼
skeleton.draw(
&global_poses, // The joint poses output from the controller
&mut debug_renderer, // gfx_debug_draw::DebugRenderer
true, // True to label each joint with their name
);
其中skeleton
是共享的骨骼实例。支持Matrix4
和DualQuaternion
。
- 使用skeletal_animation::SkinnedRenderer绘制平滑着色、纹理化的网格
// On initialization...
// To use matrices with a Linear Blend Skinning (LBS) shader
let skinned_renderer = SkinnedRenderer::<_, _, Matrix4<f32>>::from_collada(
factory, // gfx::Factory instance, to be owned by SkinnedRenderer
collada_document, // the parsed Collada document for the rigged mesh
["assets/skin.png", "assets/hair.png", "assets/eyes.png"], // Textures for each submesh in the Collada source
).unwrap();
// To use dual-quaternions with a Dual-Quaternion Linear Blend Skinning (DLB) shader
let skinned_renderer = SkinnedRenderer::<_, _, DualQuaternion<f32>>::from_collada(
factory, // gfx::Factory instance, to be owned by SkinnedRenderer
collada_document, // the parsed Collada document for the rigged mesh
["assets/skin.png", "assets/hair.png", "assets/eyes.png"], // Textures for each submesh in the Collada source
).unwrap();
...
// Later in event loop...
skinned_renderer.render(
&mut stream, // gfx::Stream
camera_view, // Matrix4<f32>
camera_projection, // <Matrix4<f32>
&global_poses // The output poses from the controller
);
参见示例演示以了解更详细的用法。
依赖关系
~14MB
~227K SLoC