CameraState.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. using UnityEngine;
  2. using Cinemachine.Utility;
  3. using System.Collections.Generic;
  4. namespace Cinemachine
  5. {
  6. /// <summary>
  7. /// The output of the Cinemachine engine for a specific virtual camera. The information
  8. /// in this struct can be blended, and provides what is needed to calculate an
  9. /// appropriate camera position, orientation, and lens setting.
  10. ///
  11. /// Raw values are what the Cinemachine behaviours generate. The correction channel
  12. /// holds perturbations to the raw values - e.g. noise or smoothing, or obstacle
  13. /// avoidance corrections. Coirrections are not considered when making time-based
  14. /// calculations such as damping.
  15. ///
  16. /// The Final position and orientation is the comination of the raw values and
  17. /// their corrections.
  18. /// </summary>
  19. public struct CameraState
  20. {
  21. /// <summary>
  22. /// Camera Lens Settings.
  23. /// </summary>
  24. public LensSettings Lens;
  25. /// <summary>
  26. /// Which way is up. World space unit vector. Must have a length of 1.
  27. /// </summary>
  28. public Vector3 ReferenceUp;
  29. /// <summary>
  30. /// The world space focus point of the camera. What the camera wants to look at.
  31. /// There is a special constant define to represent "nothing". Be careful to
  32. /// check for that (or check the HasLookAt property).
  33. /// </summary>
  34. public Vector3 ReferenceLookAt;
  35. /// <summary>
  36. /// Returns true if this state has a valid ReferenceLookAt value.
  37. /// </summary>
  38. #pragma warning disable 1718 // comparison made to same variable
  39. public bool HasLookAt => ReferenceLookAt == ReferenceLookAt; // will be false if NaN
  40. /// <summary>
  41. /// This constant represents "no point in space" or "no direction".
  42. /// </summary>
  43. public static Vector3 kNoPoint = new Vector3(float.NaN, float.NaN, float.NaN);
  44. /// <summary>
  45. /// Raw (un-corrected) world space position of this camera
  46. /// </summary>
  47. public Vector3 RawPosition;
  48. /// <summary>
  49. /// Raw (un-corrected) world space orientation of this camera
  50. /// </summary>
  51. public Quaternion RawOrientation;
  52. /// <summary>This is a way for the Body component to bypass aim damping,
  53. /// useful for when the body needs to rotate its point of view, but does not
  54. /// want interference from the aim damping. The value is the camera
  55. /// rotation, in Euler degrees.</summary>
  56. public Vector3 PositionDampingBypass;
  57. /// <summary>
  58. /// Subjective estimation of how "good" the shot is.
  59. /// Larger values mean better quality. Default is 1.
  60. /// </summary>
  61. public float ShotQuality;
  62. /// <summary>
  63. /// Position correction. This will be added to the raw position.
  64. /// This value doesn't get fed back into the system when calculating the next frame.
  65. /// Can be noise, or smoothing, or both, or something else.
  66. /// </summary>
  67. public Vector3 PositionCorrection;
  68. /// <summary>
  69. /// Orientation correction. This will be added to the raw orientation.
  70. /// This value doesn't get fed back into the system when calculating the next frame.
  71. /// Can be noise, or smoothing, or both, or something else.
  72. /// </summary>
  73. public Quaternion OrientationCorrection;
  74. /// <summary>
  75. /// Position with correction applied.
  76. /// </summary>
  77. public Vector3 CorrectedPosition => RawPosition + PositionCorrection;
  78. /// <summary>
  79. /// Orientation with correction applied.
  80. /// </summary>
  81. public Quaternion CorrectedOrientation => RawOrientation * OrientationCorrection;
  82. /// <summary>
  83. /// Position with correction applied. This is what the final camera gets.
  84. /// </summary>
  85. public Vector3 FinalPosition => RawPosition + PositionCorrection;
  86. /// <summary>
  87. /// Orientation with correction and dutch applied. This is what the final camera gets.
  88. /// </summary>
  89. public Quaternion FinalOrientation
  90. {
  91. get
  92. {
  93. if (Mathf.Abs(Lens.Dutch) > UnityVectorExtensions.Epsilon)
  94. return CorrectedOrientation * Quaternion.AngleAxis(Lens.Dutch, Vector3.forward);
  95. return CorrectedOrientation;
  96. }
  97. }
  98. /// <summary>
  99. /// Combine these hints to influence how blending is done, and how state is applied to the camera.
  100. /// </summary>
  101. public enum BlendHintValue
  102. {
  103. /// <summary>Normal state blending</summary>
  104. Nothing = 0,
  105. /// <summary>This state does not affect the camera position</summary>
  106. NoPosition = 1,
  107. /// <summary>This state does not affect the camera rotation</summary>
  108. NoOrientation = 2,
  109. /// <summary>Combination of NoPosition and NoOrientation</summary>
  110. NoTransform = NoPosition | NoOrientation,
  111. /// <summary>Spherical blend about the LookAt target (if any)</summary>
  112. SphericalPositionBlend = 4,
  113. /// <summary>Cylindrical blend about the LookAt target (if any)</summary>
  114. CylindricalPositionBlend = 8,
  115. /// <summary>Radial blend when the LookAt target changes(if any)</summary>
  116. RadialAimBlend = 16,
  117. /// <summary>Ignore the LookAt target and just slerp the orientation</summary>
  118. IgnoreLookAtTarget = 32,
  119. /// <summary>This state does not affect the lens</summary>
  120. NoLens = 64,
  121. }
  122. /// <summary>
  123. /// Combine these hints to influence how blending is done, and how state is applied to the camera.
  124. /// </summary>
  125. public BlendHintValue BlendHint;
  126. /// <summary>
  127. /// State with default values
  128. /// </summary>
  129. public static CameraState Default
  130. {
  131. get
  132. {
  133. CameraState state = new CameraState();
  134. state.Lens = LensSettings.Default;
  135. state.ReferenceUp = Vector3.up;
  136. state.ReferenceLookAt = kNoPoint;
  137. state.RawPosition = Vector3.zero;
  138. state.RawOrientation = Quaternion.identity;
  139. state.ShotQuality = 1;
  140. state.PositionCorrection = Vector3.zero;
  141. state.OrientationCorrection = Quaternion.identity;
  142. state.PositionDampingBypass = Vector3.zero;
  143. state.BlendHint = BlendHintValue.Nothing;
  144. return state;
  145. }
  146. }
  147. /// <summary>Opaque structure represent extra blendable stuff and its weight.
  148. /// The base system ignores this data - it is intended for extension modules</summary>
  149. public struct CustomBlendable
  150. {
  151. /// <summary>The custom stuff that the extension module will consider</summary>
  152. public Object m_Custom;
  153. /// <summary>The weight of the custom stuff. Must be 0...1</summary>
  154. public float m_Weight;
  155. /// <summary>Constructor with specific values</summary>
  156. /// <param name="custom">The custom stuff that the extension module will consider</param>
  157. /// <param name="weight">The weight of the custom stuff. Must be 0...1</param>
  158. public CustomBlendable(Object custom, float weight)
  159. { m_Custom = custom; m_Weight = weight; }
  160. };
  161. // This is to avoid excessive GC allocs
  162. CustomBlendable mCustom0;
  163. CustomBlendable mCustom1;
  164. CustomBlendable mCustom2;
  165. CustomBlendable mCustom3;
  166. List<CustomBlendable> m_CustomOverflow;
  167. /// <summary>The number of custom blendables that will be applied to the camera.
  168. /// The base system manages but otherwise ignores this data - it is intended for
  169. /// extension modules</summary>
  170. public int NumCustomBlendables { get; private set; }
  171. /// <summary>Get a custom blendable that will be applied to the camera.
  172. /// The base system manages but otherwise ignores this data - it is intended for
  173. /// extension modules</summary>
  174. /// <param name="index">Which one to get. Must be in range [0...NumCustomBlendables)</param>
  175. /// <returns>The custom blendable at the specified index.</returns>
  176. public CustomBlendable GetCustomBlendable(int index)
  177. {
  178. switch (index)
  179. {
  180. case 0: return mCustom0;
  181. case 1: return mCustom1;
  182. case 2: return mCustom2;
  183. case 3: return mCustom3;
  184. default:
  185. {
  186. index -= 4;
  187. if (m_CustomOverflow != null && index < m_CustomOverflow.Count)
  188. return m_CustomOverflow[index];
  189. return new CustomBlendable(null, 0);
  190. }
  191. }
  192. }
  193. int FindCustomBlendable(Object custom)
  194. {
  195. if (mCustom0.m_Custom == custom)
  196. return 0;
  197. if (mCustom1.m_Custom == custom)
  198. return 1;
  199. if (mCustom2.m_Custom == custom)
  200. return 2;
  201. if (mCustom3.m_Custom == custom)
  202. return 3;
  203. if (m_CustomOverflow != null)
  204. {
  205. for (int i = 0; i < m_CustomOverflow.Count; ++i)
  206. if (m_CustomOverflow[i].m_Custom == custom)
  207. return i + 4;
  208. }
  209. return -1;
  210. }
  211. /// <summary>Add a custom blendable to the pot for eventual application to the camera.
  212. /// The base system manages but otherwise ignores this data - it is intended for
  213. /// extension modules</summary>
  214. /// <param name="b">The custom blendable to add. If b.m_Custom is the same as an
  215. /// already-added custom blendable, then they will be merged and the weights combined.</param>
  216. public void AddCustomBlendable(CustomBlendable b)
  217. {
  218. // Attempt to merge common blendables to avoid growth
  219. int index = FindCustomBlendable(b.m_Custom);
  220. if (index >= 0)
  221. b.m_Weight += GetCustomBlendable(index).m_Weight;
  222. else
  223. index = NumCustomBlendables++;
  224. switch (index)
  225. {
  226. case 0: mCustom0 = b; break;
  227. case 1: mCustom1 = b; break;
  228. case 2: mCustom2 = b; break;
  229. case 3: mCustom3 = b; break;
  230. default:
  231. {
  232. index -= 4;
  233. if (m_CustomOverflow == null)
  234. m_CustomOverflow = new List<CustomBlendable>();
  235. if (index < m_CustomOverflow.Count)
  236. m_CustomOverflow[index] = b;
  237. else
  238. m_CustomOverflow.Add(b);
  239. break;
  240. }
  241. }
  242. }
  243. /// <summary>Intelligently blend the contents of two states.</summary>
  244. /// <param name="stateA">The first state, corresponding to t=0</param>
  245. /// <param name="stateB">The second state, corresponding to t=1</param>
  246. /// <param name="t">How much to interpolate. Internally clamped to 0..1</param>
  247. /// <returns>Linearly interpolated CameraState</returns>
  248. public static CameraState Lerp(CameraState stateA, CameraState stateB, float t)
  249. {
  250. t = Mathf.Clamp01(t);
  251. float adjustedT = t;
  252. CameraState state = new CameraState();
  253. // Combine the blend hints intelligently
  254. if (((stateA.BlendHint & stateB.BlendHint) & BlendHintValue.NoPosition) != 0)
  255. state.BlendHint |= BlendHintValue.NoPosition;
  256. if (((stateA.BlendHint & stateB.BlendHint) & BlendHintValue.NoOrientation) != 0)
  257. state.BlendHint |= BlendHintValue.NoOrientation;
  258. if (((stateA.BlendHint & stateB.BlendHint) & BlendHintValue.NoLens) != 0)
  259. state.BlendHint |= BlendHintValue.NoLens;
  260. if (((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.SphericalPositionBlend) != 0)
  261. state.BlendHint |= BlendHintValue.SphericalPositionBlend;
  262. if (((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.CylindricalPositionBlend) != 0)
  263. state.BlendHint |= BlendHintValue.CylindricalPositionBlend;
  264. if (((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.NoLens) == 0)
  265. state.Lens = LensSettings.Lerp(stateA.Lens, stateB.Lens, t);
  266. else if (((stateA.BlendHint & stateB.BlendHint) & BlendHintValue.NoLens) == 0)
  267. {
  268. if ((stateA.BlendHint & BlendHintValue.NoLens) != 0)
  269. state.Lens = stateB.Lens;
  270. else
  271. state.Lens = stateA.Lens;
  272. }
  273. state.ReferenceUp = Vector3.Slerp(stateA.ReferenceUp, stateB.ReferenceUp, t);
  274. state.ShotQuality = Mathf.Lerp(stateA.ShotQuality, stateB.ShotQuality, t);
  275. state.PositionCorrection = ApplyPosBlendHint(
  276. stateA.PositionCorrection, stateA.BlendHint,
  277. stateB.PositionCorrection, stateB.BlendHint,
  278. state.PositionCorrection,
  279. Vector3.Lerp(stateA.PositionCorrection, stateB.PositionCorrection, t));
  280. state.OrientationCorrection = ApplyRotBlendHint(
  281. stateA.OrientationCorrection, stateA.BlendHint,
  282. stateB.OrientationCorrection, stateB.BlendHint,
  283. state.OrientationCorrection,
  284. Quaternion.Slerp(stateA.OrientationCorrection, stateB.OrientationCorrection, t));
  285. // LookAt target
  286. if (!stateA.HasLookAt || !stateB.HasLookAt)
  287. state.ReferenceLookAt = kNoPoint;
  288. else
  289. {
  290. // Re-interpolate FOV to preserve target composition, if possible
  291. float fovA = stateA.Lens.FieldOfView;
  292. float fovB = stateB.Lens.FieldOfView;
  293. if (((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.NoLens) == 0
  294. && !state.Lens.Orthographic && !Mathf.Approximately(fovA, fovB))
  295. {
  296. LensSettings lens = state.Lens;
  297. lens.FieldOfView = InterpolateFOV(
  298. fovA, fovB,
  299. Mathf.Max((stateA.ReferenceLookAt - stateA.CorrectedPosition).magnitude, stateA.Lens.NearClipPlane),
  300. Mathf.Max((stateB.ReferenceLookAt - stateB.CorrectedPosition).magnitude, stateB.Lens.NearClipPlane), t);
  301. state.Lens = lens;
  302. // Make sure we preserve the screen composition through FOV changes
  303. adjustedT = Mathf.Abs((lens.FieldOfView - fovA) / (fovB - fovA));
  304. }
  305. // Linear interpolation of lookAt target point
  306. state.ReferenceLookAt = Vector3.Lerp(
  307. stateA.ReferenceLookAt, stateB.ReferenceLookAt, adjustedT);
  308. }
  309. // Raw position
  310. state.RawPosition = ApplyPosBlendHint(
  311. stateA.RawPosition, stateA.BlendHint,
  312. stateB.RawPosition, stateB.BlendHint,
  313. state.RawPosition, state.InterpolatePosition(
  314. stateA.RawPosition, stateA.ReferenceLookAt,
  315. stateB.RawPosition, stateB.ReferenceLookAt,
  316. t));
  317. // Interpolate the LookAt in Screen Space if requested
  318. if (state.HasLookAt
  319. && ((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.RadialAimBlend) != 0)
  320. {
  321. state.ReferenceLookAt = state.RawPosition + Vector3.Slerp(
  322. stateA.ReferenceLookAt - state.RawPosition,
  323. stateB.ReferenceLookAt - state.RawPosition, adjustedT);
  324. }
  325. // Clever orientation interpolation
  326. Quaternion newOrient = state.RawOrientation;
  327. if (((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.NoOrientation) == 0)
  328. {
  329. Vector3 dirTarget = Vector3.zero;
  330. if (state.HasLookAt)//&& ((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.RadialAimBlend) == 0)
  331. {
  332. // If orientations are different, use LookAt to blend them
  333. float angle = Quaternion.Angle(stateA.RawOrientation, stateB.RawOrientation);
  334. if (angle > UnityVectorExtensions.Epsilon)
  335. dirTarget = state.ReferenceLookAt - state.CorrectedPosition;
  336. }
  337. if (dirTarget.AlmostZero()
  338. || ((stateA.BlendHint | stateB.BlendHint) & BlendHintValue.IgnoreLookAtTarget) != 0)
  339. {
  340. // Don't know what we're looking at - can only slerp
  341. newOrient = Quaternion.Slerp(stateA.RawOrientation, stateB.RawOrientation, t);
  342. }
  343. else
  344. {
  345. // Rotate while preserving our lookAt target
  346. var up = state.ReferenceUp;
  347. dirTarget.Normalize();
  348. if (Vector3.Cross(dirTarget, up).AlmostZero())
  349. {
  350. // Looking up or down at the pole
  351. newOrient = Quaternion.Slerp(stateA.RawOrientation, stateB.RawOrientation, t);
  352. up = newOrient * Vector3.up;
  353. }
  354. // Blend the desired offsets from center
  355. newOrient = Quaternion.LookRotation(dirTarget, up);
  356. var deltaA = -stateA.RawOrientation.GetCameraRotationToTarget(
  357. stateA.ReferenceLookAt - stateA.CorrectedPosition, up);
  358. var deltaB = -stateB.RawOrientation.GetCameraRotationToTarget(
  359. stateB.ReferenceLookAt - stateB.CorrectedPosition, up);
  360. newOrient = newOrient.ApplyCameraRotation(Vector2.Lerp(deltaA, deltaB, adjustedT), up);
  361. }
  362. }
  363. state.RawOrientation = ApplyRotBlendHint(
  364. stateA.RawOrientation, stateA.BlendHint,
  365. stateB.RawOrientation, stateB.BlendHint,
  366. state.RawOrientation, newOrient);
  367. // Accumulate the custom blendables and apply the weights
  368. for (int i = 0; i < stateA.NumCustomBlendables; ++i)
  369. {
  370. CustomBlendable b = stateA.GetCustomBlendable(i);
  371. b.m_Weight *= (1-t);
  372. if (b.m_Weight > 0)
  373. state.AddCustomBlendable(b);
  374. }
  375. for (int i = 0; i < stateB.NumCustomBlendables; ++i)
  376. {
  377. CustomBlendable b = stateB.GetCustomBlendable(i);
  378. b.m_Weight *= t;
  379. if (b.m_Weight > 0)
  380. state.AddCustomBlendable(b);
  381. }
  382. return state;
  383. }
  384. static float InterpolateFOV(float fovA, float fovB, float dA, float dB, float t)
  385. {
  386. // We interpolate shot height
  387. float hA = dA * 2f * Mathf.Tan(fovA * Mathf.Deg2Rad / 2f);
  388. float hB = dB * 2f * Mathf.Tan(fovB * Mathf.Deg2Rad / 2f);
  389. float h = Mathf.Lerp(hA, hB, t);
  390. float fov = 179f;
  391. float d = Mathf.Lerp(dA, dB, t);
  392. if (d > UnityVectorExtensions.Epsilon)
  393. fov = 2f * Mathf.Atan(h / (2 * d)) * Mathf.Rad2Deg;
  394. return Mathf.Clamp(fov, Mathf.Min(fovA, fovB), Mathf.Max(fovA, fovB));
  395. }
  396. static Vector3 ApplyPosBlendHint(
  397. Vector3 posA, BlendHintValue hintA,
  398. Vector3 posB, BlendHintValue hintB,
  399. Vector3 original, Vector3 blended)
  400. {
  401. if (((hintA | hintB) & BlendHintValue.NoPosition) == 0)
  402. return blended;
  403. if (((hintA & hintB) & BlendHintValue.NoPosition) != 0)
  404. return original;
  405. if ((hintA & BlendHintValue.NoPosition) != 0)
  406. return posB;
  407. return posA;
  408. }
  409. static Quaternion ApplyRotBlendHint(
  410. Quaternion rotA, BlendHintValue hintA,
  411. Quaternion rotB, BlendHintValue hintB,
  412. Quaternion original, Quaternion blended)
  413. {
  414. if (((hintA | hintB) & BlendHintValue.NoOrientation) == 0)
  415. return blended;
  416. if (((hintA & hintB) & BlendHintValue.NoOrientation) != 0)
  417. return original;
  418. if ((hintA & BlendHintValue.NoOrientation) != 0)
  419. return rotB;
  420. return rotA;
  421. }
  422. Vector3 InterpolatePosition(
  423. Vector3 posA, Vector3 pivotA,
  424. Vector3 posB, Vector3 pivotB,
  425. float t)
  426. {
  427. if (pivotA == pivotA && pivotB == pivotB) // check for NaN
  428. {
  429. if ((BlendHint & BlendHintValue.CylindricalPositionBlend) != 0)
  430. {
  431. // Cylindrical interpolation about pivot
  432. var a = Vector3.ProjectOnPlane(posA - pivotA, ReferenceUp);
  433. var b = Vector3.ProjectOnPlane(posB - pivotB, ReferenceUp);
  434. var c = Vector3.Slerp(a, b, t);
  435. posA = (posA - a) + c;
  436. posB = (posB - b) + c;
  437. }
  438. else if ((BlendHint & BlendHintValue.SphericalPositionBlend) != 0)
  439. {
  440. // Spherical interpolation about pivot
  441. var c = Vector3.Slerp(posA - pivotA, posB - pivotB, t);
  442. posA = pivotA + c;
  443. posB = pivotB + c;
  444. }
  445. }
  446. return Vector3.Lerp(posA, posB, t);
  447. }
  448. }
  449. }