CubismPhysicsSubRig.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /**
  2. * Copyright(c) Live2D Inc. All rights reserved.
  3. *
  4. * Use of this source code is governed by the Live2D Open Software license
  5. * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
  6. */
  7. using Live2D.Cubism.Core;
  8. using System;
  9. using UnityEngine;
  10. namespace Live2D.Cubism.Framework.Physics
  11. {
  12. /// <summary>
  13. /// Children of rig.
  14. /// </summary>
  15. [Serializable]
  16. public class CubismPhysicsSubRig
  17. {
  18. /// <summary>
  19. /// Name.
  20. /// </summary>
  21. [SerializeField]
  22. public string Name;
  23. /// <summary>
  24. /// Input.
  25. /// </summary>
  26. [SerializeField]
  27. public CubismPhysicsInput[] Input;
  28. /// <summary>
  29. /// Original Input.
  30. /// </summary>
  31. [NonSerialized]
  32. public CubismPhysicsInput[] OriginalInput;
  33. /// <summary>
  34. /// Output.
  35. /// </summary>
  36. [SerializeField]
  37. public CubismPhysicsOutput[] Output;
  38. /// <summary>
  39. /// Original Output.
  40. /// </summary>
  41. [NonSerialized]
  42. public CubismPhysicsOutput[] OriginalOutput;
  43. /// <summary>
  44. /// Particles.
  45. /// </summary>
  46. [SerializeField]
  47. public CubismPhysicsParticle[] Particles;
  48. /// <summary>
  49. /// Normalization.
  50. /// </summary>
  51. [SerializeField]
  52. public CubismPhysicsNormalization Normalization;
  53. /// <summary>
  54. /// Rig.
  55. /// </summary>
  56. public CubismPhysicsRig Rig
  57. {
  58. get { return _rig; }
  59. set { _rig = value; }
  60. }
  61. [NonSerialized]
  62. private CubismPhysicsRig _rig;
  63. /// <summary>
  64. /// Output result of physics operations before applying to parameters.
  65. /// </summary>
  66. private struct SubRigPhysicsOutput
  67. {
  68. public float[] Output;
  69. }
  70. [NonSerialized]
  71. private SubRigPhysicsOutput _currentRigOutput; // Results of the latest pendulum calculation.
  72. [NonSerialized]
  73. private SubRigPhysicsOutput _previousRigOutput; // Result of previous pendulum calculation.
  74. /// <summary>
  75. /// Applies the specified weights from the latest and one previous result of the pendulum operation.
  76. /// </summary>
  77. /// <param name="weight">Weight of latest results.</param>
  78. public void Interpolate(float weight)
  79. {
  80. // Load input parameters.
  81. for (int i = 0; i < Output.Length; ++i)
  82. {
  83. if (Output[i].Destination == null)
  84. {
  85. var destination = Rig.Controller.Parameters.FindById(Output[i].DestinationId);
  86. if (destination == null)
  87. {
  88. continue;
  89. }
  90. Output[i].Destination = destination;
  91. }
  92. UpdateOutputParameterValue(
  93. Output[i].Destination,
  94. ref Output[i].Destination.Value,
  95. _previousRigOutput.Output[i] * (1 - weight) + _currentRigOutput.Output[i] * weight,
  96. Output[i]
  97. );
  98. }
  99. }
  100. /// <summary>
  101. /// Updates parameter from output value.
  102. /// </summary>
  103. /// <param name="parameter">Target parameter.</param>
  104. /// <param name="parameterValue">Target parameter Value.</param>
  105. /// <param name="translation">Translation.</param>
  106. /// <param name="output">Output value.</param>
  107. private void UpdateOutputParameterValue(CubismParameter parameter, ref float parameterValue, float translation, CubismPhysicsOutput output)
  108. {
  109. var outputScale = 1.0f;
  110. outputScale = output.GetScale();
  111. var value = translation * outputScale;
  112. if (value < parameter.MinimumValue)
  113. {
  114. if (value < output.ValueBelowMinimum)
  115. {
  116. output.ValueBelowMinimum = value;
  117. }
  118. value = parameter.MinimumValue;
  119. }
  120. else if (value > parameter.MaximumValue)
  121. {
  122. if (value > output.ValueExceededMaximum)
  123. {
  124. output.ValueExceededMaximum = value;
  125. }
  126. value = parameter.MaximumValue;
  127. }
  128. var weight = (output.Weight / CubismPhysics.MaximumWeight);
  129. if (weight >= 1.0f)
  130. {
  131. parameterValue = value;
  132. }
  133. else
  134. {
  135. value = (parameterValue * (1.0f - weight)) + (value * weight);
  136. parameterValue = value;
  137. }
  138. }
  139. /// <summary>
  140. /// Updates particles in every frame.
  141. /// </summary>
  142. /// <param name="strand">Particles.</param>
  143. /// <param name="totalTranslation">Total translation.</param>
  144. /// <param name="totalAngle">Total angle.</param>
  145. /// <param name="wind">Direction of wind.</param>
  146. /// <param name="thresholdValue">Value of threshold.</param>
  147. /// <param name="deltaTime">Time of delta.</param>
  148. private void UpdateParticles(
  149. CubismPhysicsParticle[] strand,
  150. Vector2 totalTranslation,
  151. float totalAngle,
  152. Vector2 wind,
  153. float thresholdValue,
  154. float deltaTime
  155. )
  156. {
  157. strand[0].Position = totalTranslation;
  158. var totalRadian = CubismPhysicsMath.DegreesToRadian(totalAngle);
  159. var currentGravity = CubismPhysicsMath.RadianToDirection(totalRadian);
  160. currentGravity.Normalize();
  161. for (var i = 1; i < strand.Length; ++i)
  162. {
  163. strand[i].Force = (currentGravity * strand[i].Acceleration) + wind;
  164. strand[i].LastPosition = strand[i].Position;
  165. // The Cubism Editor expects 30 FPS so we scale here by 30...
  166. var delay = strand[i].Delay * deltaTime * 30.0f;
  167. var direction = strand[i].Position - strand[i - 1].Position;
  168. var radian = CubismPhysicsMath.DirectionToRadian(strand[i].LastGravity, currentGravity) / CubismPhysics.AirResistance;
  169. direction.x = ((Mathf.Cos(radian) * direction.x) - (direction.y * Mathf.Sin(radian)));
  170. direction.y = ((Mathf.Sin(radian) * direction.x) + (direction.y * Mathf.Cos(radian)));
  171. strand[i].Position = strand[i - 1].Position + direction;
  172. var velocity = strand[i].Velocity * delay;
  173. var force = strand[i].Force * delay * delay;
  174. strand[i].Position = strand[i].Position + velocity + force;
  175. var newDirection = strand[i].Position - strand[i - 1].Position;
  176. newDirection.Normalize();
  177. strand[i].Position = strand[i - 1].Position + newDirection * strand[i].Radius;
  178. if (Mathf.Abs(strand[i].Position.x) < thresholdValue)
  179. {
  180. strand[i].Position.x = 0.0f;
  181. }
  182. if (delay != 0.0f)
  183. {
  184. strand[i].Velocity =
  185. ((strand[i].Position - strand[i].LastPosition) / delay) * strand[i].Mobility;
  186. }
  187. strand[i].Force = Vector2.zero;
  188. strand[i].LastGravity = currentGravity;
  189. }
  190. }
  191. /// <summary>
  192. /// Updates particles in stabilization function.
  193. /// </summary>
  194. /// <param name="strand">Particles</param>
  195. /// <param name="totalTranslation">Total translation.</param>
  196. /// <param name="totalAngle">Total angle.</param>
  197. /// <param name="wind">Direction of wind.</param>
  198. /// <param name="thresholdValue">Value of threshold.</param>
  199. private void UpdateParticlesForStabilization(
  200. CubismPhysicsParticle[] strand,
  201. Vector2 totalTranslation,
  202. float totalAngle,
  203. Vector2 wind,
  204. float thresholdValue
  205. )
  206. {
  207. strand[0].Position = totalTranslation;
  208. var totalRadian = CubismPhysicsMath.DegreesToRadian(totalAngle);
  209. var currentGravity = CubismPhysicsMath.RadianToDirection(totalRadian);
  210. currentGravity.Normalize();
  211. for (var i = 1; i < strand.Length; ++i)
  212. {
  213. strand[i].Force = (currentGravity * strand[i].Acceleration) + wind;
  214. strand[i].LastPosition = strand[i].Position;
  215. strand[i].Velocity = Vector2.zero;
  216. var force = strand[i].Force;
  217. force.Normalize();
  218. strand[i].Position = strand[i - 1].Position + force * strand[i].Radius;
  219. if (Mathf.Abs(strand[i].Position.x) < thresholdValue)
  220. {
  221. strand[i].Position.x = 0.0f;
  222. }
  223. strand[i].Force = Vector2.zero;
  224. strand[i].LastGravity = currentGravity;
  225. }
  226. }
  227. /// <summary>
  228. /// Initializes <see langword="this"/>.
  229. /// </summary>
  230. public void Initialize()
  231. {
  232. var strand = Particles;
  233. // Initialize the top of particle.
  234. strand[0].InitialPosition = Vector2.zero;
  235. strand[0].LastPosition = strand[0].InitialPosition;
  236. strand[0].LastGravity = Rig.Gravity;
  237. strand[0].LastGravity.y *= -1.0f;
  238. // Initialize particles.
  239. for (var i = 1; i < strand.Length; ++i)
  240. {
  241. var radius = Vector2.zero;
  242. radius.y = strand[i].Radius;
  243. strand[i].InitialPosition = strand[i - 1].InitialPosition + radius;
  244. strand[i].Position = strand[i].InitialPosition;
  245. strand[i].LastPosition = strand[i].InitialPosition;
  246. strand[i].LastGravity = Rig.Gravity;
  247. strand[i].LastGravity.y *= -1.0f;
  248. }
  249. // Initialize inputs.
  250. OriginalInput = new CubismPhysicsInput[Input.Length];
  251. for (var i = 0; i < Input.Length; ++i)
  252. {
  253. OriginalInput[i] = Input[i];
  254. Input[i].InitializeGetter();
  255. }
  256. _previousRigOutput = new SubRigPhysicsOutput();
  257. _currentRigOutput = new SubRigPhysicsOutput();
  258. Array.Resize(ref _previousRigOutput.Output, Output.Length);
  259. Array.Resize(ref _currentRigOutput.Output, Output.Length);
  260. // Initialize outputs.
  261. OriginalOutput = new CubismPhysicsOutput[Output.Length];
  262. for (var i = 0; i < Output.Length; ++i)
  263. {
  264. OriginalOutput[i] = Output[i];
  265. Output[i].InitializeGetter();
  266. }
  267. }
  268. /// <summary>
  269. /// Evaluate rig in every frame.
  270. /// </summary>
  271. /// <param name="deltaTime"></param>
  272. public void Evaluate(float deltaTime)
  273. {
  274. var totalAngle = 0.0f;
  275. var totalTranslation = Vector2.zero;
  276. for (var i = 0; i < Input.Length; ++i)
  277. {
  278. ref var input = ref Input[i];
  279. var weight = input.Weight / CubismPhysics.MaximumWeight;
  280. if (input.Source == null)
  281. {
  282. input.Source = Rig.Controller.Parameters.FindById(input.SourceId);
  283. input.SourceIndex = Array.IndexOf(Rig.Controller.Parameters, input.Source);
  284. }
  285. var parameter = input.Source;
  286. input.GetNormalizedParameterValue(
  287. ref totalTranslation,
  288. ref totalAngle,
  289. parameter,
  290. ref Rig.ParametersCache[input.SourceIndex],
  291. Normalization,
  292. weight
  293. );
  294. }
  295. var radAngle = CubismPhysicsMath.DegreesToRadian(-totalAngle);
  296. totalTranslation.x = (totalTranslation.x * Mathf.Cos(radAngle) - totalTranslation.y * Mathf.Sin(radAngle));
  297. totalTranslation.y = (totalTranslation.x * Mathf.Sin(radAngle) + totalTranslation.y * Mathf.Cos(radAngle));
  298. UpdateParticles(
  299. Particles,
  300. totalTranslation,
  301. totalAngle,
  302. Rig.Wind,
  303. CubismPhysics.MovementThreshold * Normalization.Position.Maximum,
  304. deltaTime
  305. );
  306. for (var i = 0; i < Output.Length; ++i)
  307. {
  308. ref var currentRigOutput = ref _currentRigOutput.Output[i];
  309. _previousRigOutput.Output[i] = currentRigOutput;
  310. ref var output = ref Output[i];
  311. if (output.Destination == null)
  312. {
  313. var destination = Rig.Controller.Parameters.FindById(output.DestinationId);
  314. if (destination == null)
  315. {
  316. continue;
  317. }
  318. output.Destination = destination;
  319. }
  320. var particleIndex = output.ParticleIndex;
  321. if (particleIndex < 1 || particleIndex >= Particles.Length)
  322. {
  323. continue;
  324. }
  325. // Update each time as the index may fluctuate.
  326. output.DestinationIndex = Array.IndexOf(Rig.Controller.Parameters, output.Destination);
  327. var translation = Particles[particleIndex].Position -
  328. Particles[particleIndex - 1].Position;
  329. var parameter = output.Destination;
  330. var outputValue = output.GetValue(
  331. translation,
  332. Particles,
  333. particleIndex,
  334. Rig.Gravity
  335. );
  336. currentRigOutput = outputValue;
  337. UpdateOutputParameterValue(parameter, ref Rig.ParametersCache[output.DestinationIndex], outputValue, output);
  338. }
  339. }
  340. /// <summary>
  341. /// Calculate the state in which the physics operation stabilizes at the current parameter values.
  342. /// </summary>
  343. public void Stabilization()
  344. {
  345. var totalAngle = 0.0f;
  346. var totalTranslation = Vector2.zero;
  347. for (var i = 0; i < Input.Length; ++i)
  348. {
  349. var weight = Input[i].Weight / CubismPhysics.MaximumWeight;
  350. if (Input[i].Source == null)
  351. {
  352. Input[i].Source = Rig.Controller.Parameters.FindById(Input[i].SourceId);
  353. }
  354. var index = Array.IndexOf(Rig.Controller.Parameters, Input[i].Source);
  355. var parameter = Input[i].Source;
  356. Input[i].GetNormalizedParameterValue(
  357. ref totalTranslation,
  358. ref totalAngle,
  359. parameter,
  360. ref Input[i].Source.Value,
  361. Normalization,
  362. weight
  363. );
  364. Rig.ParametersCache[index] = Input[i].Source.Value;
  365. }
  366. var radAngle = CubismPhysicsMath.DegreesToRadian(-totalAngle);
  367. totalTranslation.x = (totalTranslation.x * Mathf.Cos(radAngle) - totalTranslation.y * Mathf.Sin(radAngle));
  368. totalTranslation.y = (totalTranslation.x * Mathf.Sin(radAngle) + totalTranslation.y * Mathf.Cos(radAngle));
  369. UpdateParticlesForStabilization(
  370. Particles,
  371. totalTranslation,
  372. totalAngle,
  373. Rig.Wind,
  374. CubismPhysics.MovementThreshold * Normalization.Position.Maximum
  375. );
  376. for (var i = 0; i < Output.Length; ++i)
  377. {
  378. _previousRigOutput.Output[i] = _currentRigOutput.Output[i];
  379. if (Output[i].Destination == null)
  380. {
  381. var destination = Rig.Controller.Parameters.FindById(Output[i].DestinationId);
  382. if (destination == null)
  383. {
  384. continue;
  385. }
  386. Output[i].Destination = destination;
  387. }
  388. var particleIndex = Output[i].ParticleIndex;
  389. if (particleIndex < 1 || particleIndex >= Particles.Length)
  390. {
  391. continue;
  392. }
  393. var index = Array.IndexOf(Rig.Controller.Parameters, Output[i].Destination);
  394. var translation = Particles[particleIndex].Position -
  395. Particles[particleIndex - 1].Position;
  396. var parameter = Output[i].Destination;
  397. var outputValue = Output[i].GetValue(
  398. translation,
  399. Particles,
  400. particleIndex,
  401. Rig.Gravity
  402. );
  403. _currentRigOutput.Output[i] = outputValue;
  404. _previousRigOutput.Output[i] = outputValue;
  405. UpdateOutputParameterValue(parameter, ref Output[i].Destination.Value, outputValue, Output[i]);
  406. Rig.ParametersCache[index] = Output[i].Destination.Value;
  407. }
  408. }
  409. }
  410. }