Multi-Layer Animation Setup

Texturge’s multi-layer system lets every glyph own multiple independent sets of animation curve data (FPerLayerCurves), with each layer combined into the final glyph animation state according to the specified ETextAnimationBlendMode.

NOTE

Texturge has no SDF threshold or pixel layer concepts. Multi-layer means multi-layer stacking and blending of the 21-channel curve data (Override / Additive / Multiply / CrossFade).

Core Concepts

  1. Each FBakedGlyph internally stores TArray<FPerLayerCurves> LayerCurves — the curve data of each layer for that glyph (LayerCurves[0] bottom → LayerCurves[N-1] top)
  2. FBakedGlyph::Evaluate() starts from FGlyphAnimationState::Identity() and evaluates layers in order, combining per BlendMode; empty channels are skipped, preserving existing values
  3. Layers do not affect each other: each layer owns a deep copy of the UGlyphAnimation curves (ExtractCurves runs at creation), so sharing one asset across layers causes no interference

Creating Layer Assets

UGlyphAnimationLayer is created through static factories (animation asset and blend mode are required):

// Create from a schedule array
TArray<FGlyphScheduleInfo> Schedule = FAnimationCompiler::BuildScheduleInfos(Text);
UGlyphAnimationLayer* Layer = UGlyphAnimationLayer::CreateLayer(
    AnimAsset,                                   // UGlyphAnimation*
    ETextAnimationBlendMode::Override,           // blend mode (Override common for base layers)
    Schedule,                                    // schedule array
    EPlayMode::Forward,                          // play direction (default)
    1.0f,                                        // play speed (default)
    1                                            // loop count (default; 0 = infinite)
);

// Or create from text (schedule built automatically)
UGlyphAnimationLayer* LayerFromText = UGlyphAnimationLayer::CreateLayerFromText(
    AnimAsset, Text, ETextAnimationBlendMode::Additive);

Configuring Layers

Play Mode

Layer->SetPlayMode(EPlayMode::PingPong);   // play direction
Layer->SetPlayMode(EPlayMode::Forward, 2.0f, 3);   // direction + speed + loop count

NOTE

EPlayMode only has Forward / Reverse / PingPong. Looping is controlled by the third parameter NumberOfLoops (0 = infinite loop).

Scheduling (Index = -1 applies to all)

Layer->SetStartDelayAt(-1, 0.0f);   // absolute start delay
Layer->SetDelayAt(-1, 0.05f);       // per-glyph stagger (glyph i = 0 + i × 0.05)
Layer->SkipAt(-1);                  // skip all (then restore by classification)
Layer->SetSkipAnimationAt(3, false);  // restore glyph 3
Layer->SetAnimationClipAt(0.1f, 0.2f);  // front trim 0.1s, back trim 0.2s

Glyph Tweaks (Ordered Operation Steps)

10 properties × 5 operations (multiply / divide / add / subtract / clear steps) + clear all:

Layer->MultiplyScaleAt(0, FVector2D(1.2f, 1.2f));   // glyph 0 scale ×1.2
Layer->AddRotationAt(-1, 15.0f);                    // all glyphs rotation +15°
Layer->SubtractOpacityAt(2, 0.1f);                  // glyph 2 opacity -0.1
Layer->ClearGlyphOverridesAt(0);                    // clear all steps of glyph 0

Assembling the Factory

UGlyphAnimationFactory manages all layers and executes the merge bake:

// 1. Create a factory (needs a base layer + global schedule)
UGlyphAnimationFactory* Factory = UGlyphAnimationFactory::CreateFactory(BaseLayer, Schedule);
// or create from text
UGlyphAnimationFactory* FactoryFromText = UGlyphAnimationFactory::CreateFactoryFromText(BaseLayer, Text);

// 2. Add layers in order (later layers have higher priority, stack on top)
Factory->AddLayer(PositionLayer);
Factory->AddLayer(ColorAnimLayer);
Factory->AddLayer(ShadowLayer);

// 3. Run the bake (returns FBakedStage)
FBakedStage Stage = Factory->Bake();

Composing in BuildDefaultAnimation

BuildDefaultAnimation is the standard place to implement multi-layer logic. The C++ default implementation fully encapsulates the create → configure → bake flow:

FBakedStage UTextAnimInstance::BuildDefaultAnimation_Implementation(
    int32 GlyphCount, const FString& Text)
{
    TArray<FGlyphScheduleInfo> Schedule = BuildScheduleInfos(Text);

    // Base layer: Override
    UGlyphAnimationLayer* BaseLayer = CreateLayer(GlyphAnimations[0], ETextAnimationBlendMode::Override, Schedule);
    BaseLayer->SetDelayAt(-1, 0.05f);          // per-glyph stagger
    BaseLayer->SkipAt(-1);                     // restore by classification (skip whitespace)

    // Overlay layers: Additive
    UGlyphAnimationFactory* Factory = CreateFactory(BaseLayer, Schedule);
    for (int32 i = 1; i < GlyphAnimations.Num(); ++i)
    {
        UGlyphAnimationLayer* Layer = CreateLayer(GlyphAnimations[i], ETextAnimationBlendMode::Additive, Schedule);
        Factory->AddLayer(Layer);
    }

    // Factory-level tweaks (applied to the final blended state)
    Factory->MultiplyTranslationAt(-1, FVector2D(2.0f, 1.0f));

    return Factory->Bake();
}

Final Data Shape

After baking, each FBakedGlyph contains:

FBakedGlyph
  ├── LayerCurves[0]: FPerLayerCurves (21 channels) + BlendMode::Override
  ├── LayerCurves[1]: FPerLayerCurves (21 channels) + BlendMode::Additive
  ├── LayerCurves[2]: FPerLayerCurves (21 channels) + BlendMode::Multiply
  └── FactoryOverride: FGlyphCurveOverride (factory-level tweaks, applied once at evaluation)

At runtime FBakedGlyph::Evaluate() evaluates and combines layers in index order; FactoryOverride is applied once to the final state after all layers are blended (not per-layer stacking). Static glyphs (bIsStatic) do not get it applied.

images/layer-list-editor.png — Editor diagram: UGlyphAnimationFactory Layers array with base layer (Override) and additive layers, each showing the associated UGlyphAnimation asset, blend mode, play direction and loop count
images/layer-blending-diagram.png — Layer blending diagram: a glyph starting from Identity, Layer0 Override provides base transform, Layer1 Additive stacks shake offsets, Layer2 Multiply modulates scale, FactoryOverride applied once at the end, arrows marking the BlendMode combination order