CharacterKit/CharacterPreviewGrowth.cs

Component used in the editor's character preview panels. It manages a CharacterFootPlacement helper to adjust a SkinnedModelRenderer based on CharacterAppearanceData (maturity and animation state) and applies updated pose when needed. The system schedules Prepare and Apply calls on the UpdateBones stage for all instances.

namespace CharacterEditor;

/// <summary>Uses the world character's proportion pass for scene-panel previews.</summary>
public sealed class CharacterPreviewGrowth : Component
{
	readonly CharacterFootPlacement pose = new();
	SkinnedModelRenderer body;
	CharacterAppearanceData appearance;
	Model lastModel;
	string lastSequence;
	float lastMaturity = -1;
	Transform lastTransform;
	bool needsPose, wasAnimated;

	public void Configure(SkinnedModelRenderer target, CharacterAppearanceData data)
	{
		if (body != target) { pose.Dispose(); needsPose = true; }
		body = target;
		appearance = data;
		_ = Scene.GetSystem<CharacterPreviewGrowthSystem>();
	}
	internal void Prepare()
	{
		if (!body.IsValid() || appearance is null) return;
		bool animated = body.UseAnimGraph && body.PlaybackRate != 0;
		needsPose |= animated || wasAnimated != animated || lastModel != body.Model || lastSequence != body.Sequence.Name || lastMaturity != appearance.BodyMaturity || lastTransform != body.WorldTransform;
		wasAnimated = animated;
		if (needsPose) pose.Prepare(body);
	}
	internal void Apply()
	{
		if (!needsPose || !body.IsValid() || appearance is null) return;
		pose.Update(body, appearance, groundFeet: false, allowEditor: true);
		lastModel = body.Model;
		lastSequence = body.Sequence.Name;
		lastMaturity = appearance.BodyMaturity;
		lastTransform = body.WorldTransform;
		needsPose = false;
	}
	protected override void OnEnabled() => needsPose = true;
	protected override void OnDisabled() { pose.Dispose(); needsPose = true; }
	protected override void OnDestroy() => pose.Dispose();
}

public sealed class CharacterPreviewGrowthSystem : GameObjectSystem<CharacterPreviewGrowthSystem>
{
	public CharacterPreviewGrowthSystem(Scene scene) : base(scene)
	{
		Listen(Stage.UpdateBones, -100, Prepare, "Prepare preview proportions");
		Listen(Stage.UpdateBones, 100, Apply, "Apply preview proportions");
	}
	void Prepare() { foreach (var preview in Scene.GetAllComponents<CharacterPreviewGrowth>()) preview.Prepare(); }
	void Apply() { foreach (var preview in Scene.GetAllComponents<CharacterPreviewGrowth>()) preview.Apply(); }
}