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;
}
}
}
}
}