| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- /**
- * Copyright(c) Live2D Inc. All rights reserved.
- *
- * Use of this source code is governed by the Live2D Open Software license
- * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
- */
- using Live2D.Cubism.Core;
- using System;
- using UnityEngine;
- namespace Live2D.Cubism.Framework.Physics
- {
- /// <summary>
- /// Children of rig.
- /// </summary>
- [Serializable]
- public class CubismPhysicsSubRig
- {
- /// <summary>
- /// Name.
- /// </summary>
- [SerializeField]
- public string Name;
- /// <summary>
- /// Input.
- /// </summary>
- [SerializeField]
- public CubismPhysicsInput[] Input;
- /// <summary>
- /// Original Input.
- /// </summary>
- [NonSerialized]
- public CubismPhysicsInput[] OriginalInput;
- /// <summary>
- /// Output.
- /// </summary>
- [SerializeField]
- public CubismPhysicsOutput[] Output;
- /// <summary>
- /// Original Output.
- /// </summary>
- [NonSerialized]
- public CubismPhysicsOutput[] OriginalOutput;
- /// <summary>
- /// Particles.
- /// </summary>
- [SerializeField]
- public CubismPhysicsParticle[] Particles;
- /// <summary>
- /// Normalization.
- /// </summary>
- [SerializeField]
- public CubismPhysicsNormalization Normalization;
- /// <summary>
- /// Rig.
- /// </summary>
- public CubismPhysicsRig Rig
- {
- get { return _rig; }
- set { _rig = value; }
- }
- [NonSerialized]
- private CubismPhysicsRig _rig;
- /// <summary>
- /// Output result of physics operations before applying to parameters.
- /// </summary>
- private struct SubRigPhysicsOutput
- {
- public float[] Output;
- }
- [NonSerialized]
- private SubRigPhysicsOutput _currentRigOutput; // Results of the latest pendulum calculation.
- [NonSerialized]
- private SubRigPhysicsOutput _previousRigOutput; // Result of previous pendulum calculation.
- /// <summary>
- /// Applies the specified weights from the latest and one previous result of the pendulum operation.
- /// </summary>
- /// <param name="weight">Weight of latest results.</param>
- public void Interpolate(float weight)
- {
- // Load input parameters.
- for (int i = 0; i < Output.Length; ++i)
- {
- if (Output[i].Destination == null)
- {
- var destination = Rig.Controller.Parameters.FindById(Output[i].DestinationId);
- if (destination == null)
- {
- continue;
- }
- Output[i].Destination = destination;
- }
- UpdateOutputParameterValue(
- Output[i].Destination,
- ref Output[i].Destination.Value,
- _previousRigOutput.Output[i] * (1 - weight) + _currentRigOutput.Output[i] * weight,
- Output[i]
- );
- }
- }
- /// <summary>
- /// Updates parameter from output value.
- /// </summary>
- /// <param name="parameter">Target parameter.</param>
- /// <param name="parameterValue">Target parameter Value.</param>
- /// <param name="translation">Translation.</param>
- /// <param name="output">Output value.</param>
- private void UpdateOutputParameterValue(CubismParameter parameter, ref float parameterValue, float translation, CubismPhysicsOutput output)
- {
- var outputScale = 1.0f;
- outputScale = output.GetScale();
- var value = translation * outputScale;
- if (value < parameter.MinimumValue)
- {
- if (value < output.ValueBelowMinimum)
- {
- output.ValueBelowMinimum = value;
- }
- value = parameter.MinimumValue;
- }
- else if (value > parameter.MaximumValue)
- {
- if (value > output.ValueExceededMaximum)
- {
- output.ValueExceededMaximum = value;
- }
- value = parameter.MaximumValue;
- }
- var weight = (output.Weight / CubismPhysics.MaximumWeight);
- if (weight >= 1.0f)
- {
- parameterValue = value;
- }
- else
- {
- value = (parameterValue * (1.0f - weight)) + (value * weight);
- parameterValue = value;
- }
- }
- /// <summary>
- /// Updates particles in every frame.
- /// </summary>
- /// <param name="strand">Particles.</param>
- /// <param name="totalTranslation">Total translation.</param>
- /// <param name="totalAngle">Total angle.</param>
- /// <param name="wind">Direction of wind.</param>
- /// <param name="thresholdValue">Value of threshold.</param>
- /// <param name="deltaTime">Time of delta.</param>
- private void UpdateParticles(
- CubismPhysicsParticle[] strand,
- Vector2 totalTranslation,
- float totalAngle,
- Vector2 wind,
- float thresholdValue,
- float deltaTime
- )
- {
- strand[0].Position = totalTranslation;
- var totalRadian = CubismPhysicsMath.DegreesToRadian(totalAngle);
- var currentGravity = CubismPhysicsMath.RadianToDirection(totalRadian);
- currentGravity.Normalize();
- for (var i = 1; i < strand.Length; ++i)
- {
- strand[i].Force = (currentGravity * strand[i].Acceleration) + wind;
- strand[i].LastPosition = strand[i].Position;
- // The Cubism Editor expects 30 FPS so we scale here by 30...
- var delay = strand[i].Delay * deltaTime * 30.0f;
- var direction = strand[i].Position - strand[i - 1].Position;
- var radian = CubismPhysicsMath.DirectionToRadian(strand[i].LastGravity, currentGravity) / CubismPhysics.AirResistance;
- direction.x = ((Mathf.Cos(radian) * direction.x) - (direction.y * Mathf.Sin(radian)));
- direction.y = ((Mathf.Sin(radian) * direction.x) + (direction.y * Mathf.Cos(radian)));
- strand[i].Position = strand[i - 1].Position + direction;
- var velocity = strand[i].Velocity * delay;
- var force = strand[i].Force * delay * delay;
- strand[i].Position = strand[i].Position + velocity + force;
- var newDirection = strand[i].Position - strand[i - 1].Position;
- newDirection.Normalize();
- strand[i].Position = strand[i - 1].Position + newDirection * strand[i].Radius;
- if (Mathf.Abs(strand[i].Position.x) < thresholdValue)
- {
- strand[i].Position.x = 0.0f;
- }
- if (delay != 0.0f)
- {
- strand[i].Velocity =
- ((strand[i].Position - strand[i].LastPosition) / delay) * strand[i].Mobility;
- }
- strand[i].Force = Vector2.zero;
- strand[i].LastGravity = currentGravity;
- }
- }
- /// <summary>
- /// Updates particles in stabilization function.
- /// </summary>
- /// <param name="strand">Particles</param>
- /// <param name="totalTranslation">Total translation.</param>
- /// <param name="totalAngle">Total angle.</param>
- /// <param name="wind">Direction of wind.</param>
- /// <param name="thresholdValue">Value of threshold.</param>
- private void UpdateParticlesForStabilization(
- CubismPhysicsParticle[] strand,
- Vector2 totalTranslation,
- float totalAngle,
- Vector2 wind,
- float thresholdValue
- )
- {
- strand[0].Position = totalTranslation;
- var totalRadian = CubismPhysicsMath.DegreesToRadian(totalAngle);
- var currentGravity = CubismPhysicsMath.RadianToDirection(totalRadian);
- currentGravity.Normalize();
- for (var i = 1; i < strand.Length; ++i)
- {
- strand[i].Force = (currentGravity * strand[i].Acceleration) + wind;
- strand[i].LastPosition = strand[i].Position;
- strand[i].Velocity = Vector2.zero;
- var force = strand[i].Force;
- force.Normalize();
- strand[i].Position = strand[i - 1].Position + force * strand[i].Radius;
- if (Mathf.Abs(strand[i].Position.x) < thresholdValue)
- {
- strand[i].Position.x = 0.0f;
- }
- strand[i].Force = Vector2.zero;
- strand[i].LastGravity = currentGravity;
- }
- }
- /// <summary>
- /// Initializes <see langword="this"/>.
- /// </summary>
- public void Initialize()
- {
- var strand = Particles;
- // Initialize the top of particle.
- strand[0].InitialPosition = Vector2.zero;
- strand[0].LastPosition = strand[0].InitialPosition;
- strand[0].LastGravity = Rig.Gravity;
- strand[0].LastGravity.y *= -1.0f;
- // Initialize particles.
- for (var i = 1; i < strand.Length; ++i)
- {
- var radius = Vector2.zero;
- radius.y = strand[i].Radius;
- strand[i].InitialPosition = strand[i - 1].InitialPosition + radius;
- strand[i].Position = strand[i].InitialPosition;
- strand[i].LastPosition = strand[i].InitialPosition;
- strand[i].LastGravity = Rig.Gravity;
- strand[i].LastGravity.y *= -1.0f;
- }
- // Initialize inputs.
- OriginalInput = new CubismPhysicsInput[Input.Length];
- for (var i = 0; i < Input.Length; ++i)
- {
- OriginalInput[i] = Input[i];
- Input[i].InitializeGetter();
- }
- _previousRigOutput = new SubRigPhysicsOutput();
- _currentRigOutput = new SubRigPhysicsOutput();
- Array.Resize(ref _previousRigOutput.Output, Output.Length);
- Array.Resize(ref _currentRigOutput.Output, Output.Length);
- // Initialize outputs.
- OriginalOutput = new CubismPhysicsOutput[Output.Length];
- for (var i = 0; i < Output.Length; ++i)
- {
- OriginalOutput[i] = Output[i];
- Output[i].InitializeGetter();
- }
- }
- /// <summary>
- /// Evaluate rig in every frame.
- /// </summary>
- /// <param name="deltaTime"></param>
- public void Evaluate(float deltaTime)
- {
- var totalAngle = 0.0f;
- var totalTranslation = Vector2.zero;
- for (var i = 0; i < Input.Length; ++i)
- {
- ref var input = ref Input[i];
- var weight = input.Weight / CubismPhysics.MaximumWeight;
- if (input.Source == null)
- {
- input.Source = Rig.Controller.Parameters.FindById(input.SourceId);
- input.SourceIndex = Array.IndexOf(Rig.Controller.Parameters, input.Source);
- }
- var parameter = input.Source;
- input.GetNormalizedParameterValue(
- ref totalTranslation,
- ref totalAngle,
- parameter,
- ref Rig.ParametersCache[input.SourceIndex],
- Normalization,
- weight
- );
- }
- var radAngle = CubismPhysicsMath.DegreesToRadian(-totalAngle);
- totalTranslation.x = (totalTranslation.x * Mathf.Cos(radAngle) - totalTranslation.y * Mathf.Sin(radAngle));
- totalTranslation.y = (totalTranslation.x * Mathf.Sin(radAngle) + totalTranslation.y * Mathf.Cos(radAngle));
- UpdateParticles(
- Particles,
- totalTranslation,
- totalAngle,
- Rig.Wind,
- CubismPhysics.MovementThreshold * Normalization.Position.Maximum,
- deltaTime
- );
- for (var i = 0; i < Output.Length; ++i)
- {
- ref var currentRigOutput = ref _currentRigOutput.Output[i];
- _previousRigOutput.Output[i] = currentRigOutput;
- ref var output = ref Output[i];
- if (output.Destination == null)
- {
- var destination = Rig.Controller.Parameters.FindById(output.DestinationId);
- if (destination == null)
- {
- continue;
- }
- output.Destination = destination;
- }
- var particleIndex = output.ParticleIndex;
- if (particleIndex < 1 || particleIndex >= Particles.Length)
- {
- continue;
- }
- // Update each time as the index may fluctuate.
- output.DestinationIndex = Array.IndexOf(Rig.Controller.Parameters, output.Destination);
- var translation = Particles[particleIndex].Position -
- Particles[particleIndex - 1].Position;
- var parameter = output.Destination;
- var outputValue = output.GetValue(
- translation,
- Particles,
- particleIndex,
- Rig.Gravity
- );
- currentRigOutput = outputValue;
- UpdateOutputParameterValue(parameter, ref Rig.ParametersCache[output.DestinationIndex], outputValue, output);
- }
- }
- /// <summary>
- /// Calculate the state in which the physics operation stabilizes at the current parameter values.
- /// </summary>
- public void Stabilization()
- {
- var totalAngle = 0.0f;
- var totalTranslation = Vector2.zero;
- for (var i = 0; i < Input.Length; ++i)
- {
- var weight = Input[i].Weight / CubismPhysics.MaximumWeight;
- if (Input[i].Source == null)
- {
- Input[i].Source = Rig.Controller.Parameters.FindById(Input[i].SourceId);
- }
- var index = Array.IndexOf(Rig.Controller.Parameters, Input[i].Source);
- var parameter = Input[i].Source;
- Input[i].GetNormalizedParameterValue(
- ref totalTranslation,
- ref totalAngle,
- parameter,
- ref Input[i].Source.Value,
- Normalization,
- weight
- );
- Rig.ParametersCache[index] = Input[i].Source.Value;
- }
- var radAngle = CubismPhysicsMath.DegreesToRadian(-totalAngle);
- totalTranslation.x = (totalTranslation.x * Mathf.Cos(radAngle) - totalTranslation.y * Mathf.Sin(radAngle));
- totalTranslation.y = (totalTranslation.x * Mathf.Sin(radAngle) + totalTranslation.y * Mathf.Cos(radAngle));
- UpdateParticlesForStabilization(
- Particles,
- totalTranslation,
- totalAngle,
- Rig.Wind,
- CubismPhysics.MovementThreshold * Normalization.Position.Maximum
- );
- for (var i = 0; i < Output.Length; ++i)
- {
- _previousRigOutput.Output[i] = _currentRigOutput.Output[i];
- if (Output[i].Destination == null)
- {
- var destination = Rig.Controller.Parameters.FindById(Output[i].DestinationId);
- if (destination == null)
- {
- continue;
- }
- Output[i].Destination = destination;
- }
- var particleIndex = Output[i].ParticleIndex;
- if (particleIndex < 1 || particleIndex >= Particles.Length)
- {
- continue;
- }
- var index = Array.IndexOf(Rig.Controller.Parameters, Output[i].Destination);
- var translation = Particles[particleIndex].Position -
- Particles[particleIndex - 1].Position;
- var parameter = Output[i].Destination;
- var outputValue = Output[i].GetValue(
- translation,
- Particles,
- particleIndex,
- Rig.Gravity
- );
- _currentRigOutput.Output[i] = outputValue;
- _previousRigOutput.Output[i] = outputValue;
- UpdateOutputParameterValue(parameter, ref Output[i].Destination.Value, outputValue, Output[i]);
- Rig.ParametersCache[index] = Output[i].Destination.Value;
- }
- }
- }
- }
|