using System; using System.Collections.Generic; namespace Cthangover.Live2D.Cubism.Framework { /// /// Defines a sinusoidal breathing parameter. Each breath parameter /// oscillates a model parameter value around an offset with a given /// peak amplitude, cycle period, and weight. /// public struct BreathParameterData { /// Model parameter ID to apply the breath to. public string ParameterId; /// Base offset added to the sinusoidal value. public float Offset; /// Amplitude of the sine wave. public float Peak; /// Full cycle duration in seconds. public float Cycle; /// Multiplier applied to the final breath value. public float Weight; } /// /// Applies sinusoidal breathing animation to model parameters. /// Operates additively on top of existing parameter values — /// the breath oscillation is multiplied by /// and added to the current parameter value every frame. /// /// Configured with a list of entries, /// each targeting a specific parameter ID. Called from /// alongside other effect layers /// (eye blink, pose, physics). /// public class CubismBreath { private readonly List _parameters = new(); private float _currentTime; /// /// Replaces the current breath parameter set. /// Previously configured parameters are cleared. /// public void SetParameters(List parameters) { _parameters.Clear(); _parameters.AddRange(parameters); } /// /// Applies the breath oscillation to matching model parameters. /// Accumulates internal time and evaluates a sine wave per parameter: /// offset + peak * sin(2 * pi * time / cycle), then adds /// the weighted result to the native parameter value. /// public unsafe void UpdateParameters(CubismNativeModel model, float deltaTimeSeconds) { _currentTime += deltaTimeSeconds; var idsPtr = model.GetParameterIds(); var values = model.GetParameterValues(); for (int i = 0; i < model.ParameterCount; i++) { var id = CubismNativeModel.ReadStringFromPtrArray(idsPtr, i); foreach (var bp in _parameters) { if (bp.ParameterId != id) continue; var value = bp.Offset + bp.Peak * MathF.Sin(2f * MathF.PI * _currentTime / bp.Cycle); values[i] += bp.Weight * value; break; } } } } }