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 游戏开发

Download history 13/week @ 2024-03-30

140 每月下载量

MIT 许可证

87KB
1.5K SLoC

skeletal_animation

Build Status

一个用于数据驱动的骨骼动画的 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是自上次更新以来的外推时间。要实际使用骨骼姿态渲染内容,您可以:

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是共享的骨骼实例。支持Matrix4DualQuaternion

  • 使用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