CD Code Digest Hub /GTA V

动画数据、运动网络、姿态依赖图与物理角色

范围:Base cranimation/crskeleton/crbody/creature/crmetadata;Suite crclip/crmotiontree/crparameterizedmotion/move/crextra/crposedb;Framework fwanimation;NaturalMotion 集成。静态分析,未运行。入口:Summary。

17 分钟阅读 14 个章节 2 张图

1. 这里的动画是多阶段计算系统#

RAGE 把可共享资产、每角色播放状态、姿态计算图、骨架/组件输出与游戏协调分开。角色“正在播什么”和“本帧最终骨骼矩阵是什么”属于不同层,动作状态更新和完整 pose 合成也可以有不同完成点。

move network 指动作控制图,不是网络通信模块。crClip 不只是原始关键帧:它封装元数据并隐藏底层动画或表达式存储。crCreature 也不限于骨架,还可输出 mover、灯光、粒子、blendshape、shader 变量等组件。

2. 共享定义与实例状态#

crSkeletonData/bone data 表达共享骨架结构,crSkeleton 持有具体姿态;animation、clip dictionary、move network definition 可作为资源共享,player/network/director 维护实例状态。

mvNetworkDef::Create 的注释专门要求调用者持有 outputBuffer 的所有权,直到网络更新结束并被销毁。可见“定义复用”不等于“输出缓冲自动归引擎管理”;异步生命周期必须跟随真实使用者。

Clip 引用还区分只引用 clip 与连同 dictionary 的引用。模型、动画字典和任务对象之间的共享关系直接决定 streaming 能否卸载。进入动作时只保留 clip 裸指针,忽略字典生命期,会破坏资源系统的假设。

3. 压缩不仅减少文件大小,还改变采样热路径#

crAnimChannel 有 raw/static float、vector、quaternion、量化、变长量化、间接量化、RLE、线性和 B-spline 等类型,并提供 compression/decompression cost 元信息。chunk 表示短时间段,packer 再将编辑表示整理成运行时 block stream。

crAnimation::IsRaw 的文档明确:raw 只经历无损压缩,cooked 包含有损处理,不能完全恢复为可编辑原稿。压缩容差应根据骨骼/通道用途选择,而不是给整个人体一个统一阈值。

Smallest-three quaternion 编码保存三个分量并重建另一分量,利用单位四元数约束减少数据;packer 内有重建和 normalize 索引组织。但 channelsmallestthree.h 属于 CR_DEV 条件下的通道描述,不能把该编辑对象虚函数调用当成发行版最终采样路径。应继续看 packer/block stream 和 iterator 如何解码。

收益与限制:

手段主要收益需要观察的代价
静态通道提取恒定骨骼几乎不占动态采样流量需要正确恢复默认 DOF
量化与紧凑 block减少存储、驻留与读取带宽末端误差、重建 CPU、误差跨骨链放大
时间 chunk可局部采样,避免解压整段随机跳转和循环边界缓存失效
nearest frame 选项可能减少插值成本接口明确提示结果不够平滑
mover 专门处理直接得到运动增量跨循环、混合和时间变化必须一致

本仓库不能仅通过 codec 类型给出压缩率。需要真实动画资产、容差配置和工具输出才能测量。

4. Frame 是 DOF 容器,不等于一张骨骼矩阵数组#

CompositeFrame 根据 frame 的 DOF 映射填入采样结果;未匹配轨道默认清零,clear=false 时保留旧值,调用方必须先初始化或正确处理。CompositeFrameWithMover 额外将 mover DOF 转换为 delta。

这个约定使 partial-body、过滤、表达式和非骨骼参数能够共用 frame 流水线,但也会产生隐蔽错误:上帧残留、不同 skeleton 的索引映射、没有初始化的缺失通道,都可能在最终渲染前才表现出来。

frame accelerator、缓存与索引锁减少每次根据骨名查找的开销。其收益来自“提前构造和复用映射”,因此骨架重建、换装、LOD 切换或资源卸载必须正确失效这些映射。

5. Clip 元数据让动画具有玩法语义#

Clip 包含 properties、tags、events,可以让脚步音、面部事件、同步窗口或脚本可见属性跟随动画时间。fwAnimDirector 读取 VisibleToScript 属性并处理 phase 区间,包括跨循环的区间查找。

所以只“停止不可见角色骨骼采样”不意味着可以停止所有动作更新:时间推进、事件、root motion 和玩法状态可能仍必须继续。事件的 exactly-once/跨循环语义需要在时间跳跃、倒放、回放和延迟补 pose 时单独检查。

6. Move network 与参数化运动#

Move 将 definition 与 instance 分离,提供状态、子网络和参数缓冲。signal 中 flags 是持续开关,requests 每次更新后重置,需要调用者继续推入才会保持。把一次 request 当成永久状态,会得到与设计相反的行为。

参数化运动层提供 parameterization、weights、sampler、registration curve、time alignment 等对象,将输入参数映射到动作片段及其混合和时间配准。它的价值是让速度、方向等连续变化能够共享一组动作,避免离散状态爆炸。

但参数维数增长会增加内容覆盖和插值难度;稀疏采样可能出现脚滑或过渡不自然,时间对齐会影响步频和根运动。文件存在能证明能力,具体 GTA 动作如何使用仍须结合动作网络定义和资产,当前集合缺少完整运行资产,不能重建所有角色行为图。

7. Motion tree 先表达意图,Composer 再表达执行依赖#

crmtComposer 明确说明:从 motion node tree 创建 dependency tree。它提供 Animation、Blend/Merge/Add、Filter、Frame、Normalize、Skeleton、IK、Physical、Mirror、Extrapolate、ShaderVars 等操作。节点图体现动作组合语义,依赖图体现输入结果何时就绪以及临时 frame 何时可释放。

当前 composer 的结构上限包括 256 dependencies、65 栈深容量、128 frame 编号、12 个 used-frame 相关容量和 56 个 proxy 条目。它们属于不同内部结构,不能把 128 简化成“可同时存在 128 张完整 pose”,也不能将这些数字当作全部平台永久规格。

proxy 节点缓存、frame allocator/releaser 数组和 ComputeFrameOffsets 表明系统不仅安排计算,也安排临时结果的存活区间。一个 blend tree 的成本因此由 活跃节点、临时 pose 峰值、共享结果和依赖深度共同决定,而不是编辑器中总节点数。

新项目应为动画图编译器增加静态峰值估算,检查最坏过渡叠加、嵌套子图与 proxy 共享;运行时同时记录真实活跃依赖数。把每个节点都分配独立堆对象和完整 frame 往往会丢掉这套设计的重要收益。

8. 调度:Update、Compose 与 Recycle 各有完成语义#

crmtMotionTreeScheduler::Schedule 断言只能由主线程调度,检查前一轮状态 Idle,然后建立 Update → 可选 Compose → Recycle 的依赖链。Compose 通过 scratch 创建更细的依赖节点;Recycle 负责结束流程和回调。

IsComplete(updateOnly) 区分状态更新完成与整个流程完成。WaitOnComplete 支持提高优先级,等待线程通过注册的 semaphore 被通知。父 motion tree 参数又允许附属对象的计算等待父对象正确完成。

图用于说明完成层次,并非每棵树都含两个采样节点。主线程调度并不代表节点在主线程执行;同样,等待状态更新完成不代表骨架 pose 已安全读取。

9. Director 处理物理前、物理中、渲染前和镜头后#

fwAnimDirector 暴露 PrePhysics、MidPhysics、PreRender 和 PostCamera 的 Start/Wait 接口。它将 creature、move、expressions、facial rig、ragdoll、synced scene 等 component 组合起来。

这解决一个实际矛盾:角色运动要尽早提供给物理,而镜头相关 IK、最终可见性或某些附加输出要更晚确定。阶段化使不同消费者在需要时等待对应结果,而不是统一在帧初完成所有角色的全量 pose。

StartUpdatePostCamera 对已更新角色把 deltaTime 置零,或者只安排 late compose,避免同一帧再次推进动作时间。它说明“补算 pose”和“推进动画状态”必须分离。

但需要谨慎看待旧注释:当前 UpdateMove 的 ignoreVisibilityTest 标为 UNUSED_PARAM,结尾同时把 m_Updated 与 m_PoseUpdated 置 true。因此不能只凭保留的 mover-only 注释断言该入口现版本一直按不可见角色跳过完整 pose。具体裁剪还要看调用方与组件配置。报告将其作为框架能力和时序设计,而非已量测的全局动画 LOD 策略。

10. 动画与物理的双向连接#

Ragdoll component 从 creature skeleton 做 inverse pose,保存身体和根 frame,再将 frame 传回 move 或应用回 skeleton;包含对额外 DOF 的处理。NaturalMotion fragment manager 与游戏物理分成 StepPhase1/StepPhase2,动画速度和身体接触参与行为控制。

这个体系的特征是“有行为控制的物理角色”与关键帧系统共存。过渡时要处理根位置、根速度、骨骼姿态、关节体、碰撞 bound 及动画事件的一致性。突然把整个人体从物理矩阵复制回动画默认姿态,会产生位置跳变或速度不连续。

本报告没有把整个 NaturalMotion 内部行为库当成已逐行为审计;能确认的是接口、两阶段集成和姿态/速度通路。接触求解与 articulated 细节见物理。

11. Pose matching 的真实能力与规模边界#

crPoseMatcher 支持从 skeleton 和历史 skeleton 构建查询,样本含 key、time 和 point cloud。MatchPointCloud 当前实现遍历所有样本,通过 PreFilter 获取权重,计算带 early-out 的点云距离,再用 PostFilter 选择结果;返回 clip 身份、时间和对齐 transform。

它是实实在在的姿态匹配能力,可服务倒地后的起身衔接等用途;Ragdoll component 也装入相关 pose matcher 资源。但这段实现是线性扫描候选,不能据此称为现代大规模、带未来轨迹特征和索引加速的完整 motion-matching locomotion。

复杂度随候选数量和点云比较成本增长。扩大数据库时应测过滤效果、early-out 命中与最坏候选数;若目标是每角色每帧搜索数万片段,需要设计特征归一化、近邻索引、轨迹与接触约束、数据库 streaming,而不只是换一个更多样本的文件。

12. 缓存、带宽与动画性能模型#

crAnimCache 缓存解压 block 和 frame indices,底层使用 thread-safe accelerator。缺省按需创建路径仅配置 4 个 blocks,但允许通过 SetInstance 注入外部 cache;这绝不是“GTA 全局只缓存四段动画”的证据。

性能可以按以下分析模型拆解:

角色成本 ≈ 状态图更新 + 活跃 clip 解码 + DOF 混合 + IK/表达式 + 骨架传播 + 物理交换 + 调度/等待。

大量同装扮 NPC 不一定共享解码结果:播放时间、动作层、参数、骨架变化不同会改变复用条件。反过来,短而多的不同 clip 可能造成缓存抖动,少量长图也可能由依赖深度控制整帧尾部。

优化方向必须保留的语义适用边界
降低不可见 pose 频率时间、事件、root motion、碰撞需求镜头切换需补 pose,不能时间推进两次
过滤非必要 DOF附件、IK、物理仍依赖的骨骼需要依赖闭包,而非只删不可见 mesh 的骨
共享/缓存采样结果phase、骨架映射、曲线参数一致人群不同步时命中率下降
动画压缩足底、手持物、面部误差要求最小文件体积未必最省 CPU
延迟并行计算结果在消费前完成等待太晚会形成帧尾长链

13. 新项目的验证矩阵#

应同时准备:稀疏/密集人群、多人同动作/完全不同动作、换装和骨架 LOD、快速镜头切换、过场同步、ragdoll 起身、低帧率与时间缩放。记录每阶段的角色数、活跃节点、临时 frame 峰值、cache misses、解码/IK 时间和 wait 时间。

质量验证要观察脚滑、末端误差、根轨迹、事件重复/遗漏和物理切换连续性。只测渲染帧率会漏掉这些问题。本次未执行上述实验,也不提供未经测量的角色承载数量。

14. 源码证据#

文件关键证据
animation.h92–114 Frame/mover 契约;136–139 raw/cooked
animchannel.h26–108 通道类型及成本
animpacker.cpp索引、quantize、重建/normalize 和 legacy 转换
animcache.h缓存接口;92–113 可替换缺省实例
clip.h38–68 元数据、类型和引用选项
move_network.h43–48 request/flag;95–103 outputBuffer 生命周期
parameterizedmotion.h数据与实例分离,参数化动作抽象
composer.h35–109 依赖生成、操作和容量
motiontreescheduler.cpp79–175 Schedule/Attach;203 起完成与等待
animdirector.cpp242–319 晚期更新;381–416 当前 UpdateMove;1613 起阶段等待
directorcomponentragdoll.cpppose matcher 装入、129 起姿态互转
posematcher.cpp265/299 skeleton 查询;472–517 线性样本匹配
NaturalMotion manager.hfragment/NM manager 的阶段接口

关联:多线程、流式加载、性能。