烘焙管线概览
Texturge 的烘焙管线将 UTextAnimationBlueprint 与指定文本编译为预计算的 FBakedAnimation 数据。烘焙在需要时(控件初始化、文本变化、蓝图编译后)完成,运行时仅做高效求值。
NOTE
Texturge 不存在字体 → SDF 纹理烘焙管线。动画编译管线处理的是逐字形曲线数据,不涉及 GPU 纹理生成。
管线入口
FAnimationCompiler::BakeAllStages() 是烘焙管线的唯一入口:
struct TEXTURGE_API FAnimationCompiler
{
static constexpr int32 MinFramesBetweenRebakes = 3;
using FAnimInstanceCustomizer = TFunction<void(UTextAnimInstance*)>;
static FBakedAnimation BakeAllStages(
UTextAnimationBlueprint* Blueprint,
const FString& Text,
const FAnimInstanceCustomizer& InstanceCustomizer = nullptr);
};
第三个参数 InstanceCustomizer 用于注入烘焙临时实例(如应用参数覆盖),蓝图 CDO 永不修改。
管线阶段
第一阶段:构建调度信息
BuildScheduleInfos(Text)遍历文本全部 Unicode 码点,生成TArray<FGlyphScheduleInfo>- 预填每个字形的索引、字符串与分类(Whitespace / Punctuation / CJK / Decorator)
- 分类由
Texturge::Unicode工具判定,代理对(Surrogate Pair Emoji)映射为单字形
第二阶段:蓝图事件烘焙
- 创建临时
UTextAnimInstance(Blueprint->GeneratedClass) - 若传入
InstanceCustomizer,先应用到临时实例(参数覆盖注入点) ProcessEvent调用 BP 覆盖的BuildDefaultAnimation(GlyphCount, Text)- 蓝图覆盖返回无效结果时,回退到 C++ 默认
_Implementation:
BuildScheduleInfos(Text)
→ CreateLayer(GlyphAnimations[0], Override, Schedule) ← 基础层
→ CreateFactory(BaseLayer, Schedule)
→ for i in 1..N: Factory->AddLayer(CreateLayer(GlyphAnimations[i], Additive, Schedule))
→ Factory->Bake() → FBakedStage
第三阶段:工厂烘焙(UGlyphAnimationFactory::Bake)
Bake() 返回 FBakedStage,执行三步管线:
STEP 1: 逐层独立烘焙
for each Layer: BakeStageWithCurves(Layer.Curves, Layer.Schedule)
→ 每个 Glyph 获得单层烘焙结果
STEP 1.5: 消费 Layer 级有序微调步骤
for each Layer: ApplyGlyphOverrideSteps(Layer.GlyphCurveOverrides)
→ 按序对 TArray<FRichCurveKey> 执行 Multiply/Divide/Add/Subtract
→ 乘除: Value/Tangent/Weight 等比缩放(加权贝塞尔切线)
→ 加减: 仅 Value
→ 空轨道: 基于已有键时间范围创建恒等双键
STEP 2: 逐 Glyph 逐层拷贝
for each Glyph:
for each Layer:
SrcCurveSet.OffsetBy(LayerStartTime)
FBakedGlyph.AddLayer() → FPerLayerCurves
→ 不合并曲线,每层保留独立曲线数据
STEP 3: 工厂级全局后处理
3a: ApplyFactorySchedule(Skip/StartDelay) — 作用于合并后 Glyph StartTime
3b: Factory.GlyphCurveOverrides 烘焙进 FBakedGlyph.FactoryOverride
→ 求值时对混合后的最终状态整体应用一次
3c: 动画剪切(SetAnimationClipAt)— 前端/末端裁剪(播放轴口径)
→ 跳过 bIsStatic Glyph
NOTE
动画剪切(0.3.0):层与工厂级
SetAnimationClipAt(FrontTrim, BackTrim)按播放轴口径裁剪,有限循环展开物化(只改最后一次循环),全裁字形静态化。
第四阶段:组装最终输出
- 组装
FBakedAnimation:DefaultStage由BuildDefaultAnimation返回,IntroStage/OutroStage为 nullptr - 计算双指纹:
TextFingerprint = CityHash64(SourceText)、BlueprintFingerprint(见下) - 返回
FBakedAnimation(move 语义,支持 Double-Buffer 发布)
指纹缓存
| 指纹 | 计算方式 | 失效条件 |
|---|---|---|
TextFingerprint | CityHash64(SourceText) | 文本内容变化 |
BlueprintFingerprint | 哈希:路径名 + 所有 GlyphAnimation 资产签名(MovieScene + 所有 Track/Section 的 GetSignature())+ 所有 BlueprintVisible CDO 属性值 + StageTransitionConfig + CompileCount + GeneratedClass 指针地址 | 蓝图编译 / 参数变化 |
关键机制:
- Track/Section 签名遍历:Sequencer 编辑仅更新 Section 签名,不更新父级 MovieScene 签名——指纹必须遍历所有子对象
- CompileCount:每次编译递增的
uint32,确保 UE 编译实例重用时地址不变的情况下指纹仍然变化 - 重烘焙冷却:
MinFramesBetweenRebakes = 3帧,防止参数扫频时洪水重烘焙 - 缓存失效职责在控件层 / 编辑器预览层(
FTexturgePreviewBakeCache),编译期不负责缓存刷新策略
TIP
通过
SetBlueprintVariable修改参数后,指纹变化触发重烘焙,但受 3 帧冷却约束——高频参数扫频时最多延迟 3 帧生效。