CameraState.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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 { get; set; }
  25. /// <summary>
  26. /// Which way is up. World space unit vector. Must have a length of 1.
  27. /// </summary>
  28. public Vector3 ReferenceUp { get; set; }
  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 { get; set; }
  35. /// <summary>
  36. /// Returns true if this state has a valid ReferenceLookAt value.
  37. /// </summary>
  38. public bool HasLookAt { get { return ReferenceLookAt == ReferenceLookAt; } } // will be false if NaN
  39. /// <summary>
  40. /// This constant represents "no point in space" or "no direction".
  41. /// </summary>
  42. public static Vector3 kNoPoint = new Vector3(float.NaN, float.NaN, float.NaN);
  43. /// <summary>
  44. /// Raw (un-corrected) world space position of this camera
  45. /// </summary>
  46. public Vector3 RawPosition { get; set; }
  47. /// <summary>
  48. /// Raw (un-corrected) world space orientation of this camera
  49. /// </summary>
  50. public Quaternion RawOrientation { get; set; }
  51. /// <summary>This is a way for the Body component to bypass aim damping,
  52. /// useful for when the body needs to rotate its point of view, but does not
  53. /// want interference from the aim damping. The value is the camera
  54. /// rotation, in Euler degrees.</summary>
  55. public Vector3 PositionDampingBypass { get; set; }
  56. /// <summary>
  57. /// Subjective estimation of how "good" the shot is.
  58. /// Larger values mean better quality. Default is 1.
  59. /// </summary>
  60. public float ShotQuality { get; set; }
  61. /// <summary>
  62. /// Position correction. This will be added to the raw position.
  63. /// This value doesn't get fed back into the system when calculating the next frame.
  64. /// Can be noise, or smoothing, or both, or something else.
  65. /// </summary>
  66. public Vector3 PositionCorrection { get; set; }
  67. /// <summary>
  68. /// Orientation correction. This will be added to the raw orientation.
  69. /// This value doesn't get fed back into the system when calculating the next frame.
  70. /// Can be noise, or smoothing, or both, or something else.
  71. /// </summary>
  72. public Quaternion OrientationCorrection { get; set; }
  73. /// <summary>
  74. /// Position with correction applied.
  75. /// </summary>
  76. public Vector3 CorrectedPosition { get { return RawPosition + PositionCorrection; } }
  77. /// <summary>
  78. /// Orientation with correction applied.
  79. /// </summary>
  80. public Quaternion CorrectedOrientation { get { return RawOrientation * OrientationCorrection; } }
  81. /// <summary>
  82. /// Position with correction applied. This is what the final camera gets.
  83. /// </summary>
  84. public Vector3 FinalPosition { get { return RawPosition + PositionCorrection; } }
  85. /// <summary>
  86. /// Orientation with correction and dutch applied. This is what the final camera gets.
  87. /// </summary>
  88. public Quaternion FinalOrientation
  89. {
  90. get
  91. {
  92. if (Mathf.Abs(Lens.Dutch) > UnityVectorExtensions.Epsilon)
  93. return CorrectedOrientation * Quaternion.AngleAxis(Lens.Dutch, Vector3.forward);
  94. return CorrectedOrientation;
  95. }
  96. }
  97. /// <summary>
  98. /// These hints can be or'ed toether to influence how blending is done, and how state
  99. /// 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. /// These hints can be or'ed toether to influence how blending is done, and how state
  124. /// is applied to the camera
  125. /// </summary>
  126. public BlendHintValue BlendHint { get; set; }
  127. /// <summary>
  128. /// State with default values
  129. /// </summary>
  130. public static CameraState Default
  131. {
  132. get
  133. {
  134. CameraState state = new CameraState();
  135. state.Lens = LensSettings.Default;
  136. state.ReferenceUp = Vector3.up;
  137. state.ReferenceLookAt = kNoPoint;
  138. state.RawPosition = Vector3.zero;
  139. state.RawOrientation = Quaternion.identity;
  140. state.ShotQuality = 1;
  141. state.PositionCorrection = Vector3.zero;
  142. state.OrientationCorrection = Quaternion.identity;
  143. state.PositionDampingBypass = Vector3.zero;
  144. state.BlendHint = BlendHintValue.Nothing;
  145. return state;
  146. }
  147. }
  148. /// <summary>Opaque structure represent extra blendable stuff and its weight.
  149. /// The base system ignores this data - it is intended for extension modules</summary>
  150. public struct CustomBlendable
  151. {
  152. /// <summary>The custom stuff that the extension module will consider</summary>
  153. public Object m_Custom;
  154. /// <summary>The weight of the custom stuff. Must be 0...1</summary>
  155. public float m_Weight;
  156. /// <summary>Constructor with specific values</summary>
  157. /// <param name="custom">The custom stuff that the extension module will consider</param>
  158. /// <param name="weight">The weight of the custom stuff. Must be 0...1</param>
  159. public CustomBlendable(Object custom, float weight)
  160. { m_Custom = custom; m_Weight = weight; }
  161. };
  162. // This is to avoid excessive GC allocs
  163. CustomBlendable mCustom0;
  164. CustomBlendable mCustom1;
  165. CustomBlendable mCustom2;
  166. CustomBlendable mCustom3;
  167. List<CustomBlendable> m_CustomOverflow;
  168. /// <summary>The number of custom blendables that will be applied to the camera.
  169. /// The base system manages but otherwise ignores this data - it is intended for
  170. /// extension modules</summary>
  171. public int NumCustomBlendables { get; private set; }
  172. /// <summary>Get a custom blendable that will be applied to the camera.
  173. /// The base system manages but otherwise ignores this data - it is intended for
  174. /// extension modules</summary>
  175. /// <param name="index">Which one to get. Must be in range [0...NumCustomBlendables)</param>
  176. /// <returns>The custom blendable at the specified index.</returns>
  177. public CustomBlendable GetCustomBlendable(int index)
  178. {
  179. switch (index)
  180. {
  181. case 0: return mCustom0;
  182. case 1: return mCustom1;
  183. case 2: return mCustom2;
  184. case 3: return mCustom3;
  185. default:
  186. {
  187. index -= 4;
  188. if (m_CustomOverflow != null && index < m_CustomOverflow.Count)
  189. return m_CustomOverflow[index];
  190. return new CustomBlendable(null, 0);
  191. }
  192. }
  193. }
  194. int FindCustomBlendable(Object custom)
  195. {
  196. if (mCustom0.m_Custom == custom)
  197. return 0;
  198. if (mCustom1.m_Custom == custom)
  199. return 1;
  200. if (mCustom2.m_Custom == custom)
  201. return 2;
  202. if (mCustom3.m_Custom == custom)
  203. return 3;
  204. if (m_CustomOverflow != null)
  205. {
  206. for (int i = 0; i < m_CustomOverflow.Count; ++i)
  207. if (m_CustomOverflow[i].m_Custom == custom)
  208. return i + 4;
  209. }
  210. return -1;
  211. }
  212. /// <summary>Add a custom blendable to the pot for eventual application to the camera.
  213. /// The base system manages but otherwise ignores this data - it is intended for
  214. /// extension modules</summary>
  215. /// <param name="b">The custom blendable to add. If b.m_Custom is the same as an
  216. /// already-added custom blendable, then they will be merged and the weights combined.</param>
  217. public void AddCustomBlendable(CustomBlendable b)
  218. {
  219. // Attempt to merge common blendables to avoid growth
  220. int index = FindCustomBlendable(b.m_Custom);
  221. if (index >= 0)
  222. b.m_Weight += GetCustomBlendable(index).m_Weight;
  223. else
  224. {
  225. index = NumCustomBlendables;
  226. NumCustomBlendables = index + 1;
  227. }
  228. switch (index)
  229. {
  230. case 0: mCustom0 = b; break;
  231. case 1: mCustom1 = b; break;
  232. case 2: mCustom2 = b; break;
  233. case 3: mCustom3 = b; break;
  234. default:
  235. {
  236. if (m_CustomOverflow == null)
  237. m_CustomOverflow = new List<CustomBlendable>();
  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 = UnityQuaternionExtensions.SlerpWithReferenceUp(
  342. stateA.RawOrientation, stateB.RawOrientation, t, state.ReferenceUp);
  343. }
  344. else
  345. {
  346. var blendUp = Vector3.Slerp(
  347. stateA.RawOrientation * Vector3.up, stateB.RawOrientation * Vector3.up, t);
  348. // Rotate while preserving our lookAt target
  349. if (Vector3.Cross(dirTarget, blendUp).AlmostZero())
  350. {
  351. // Looking up or down at the pole
  352. newOrient = UnityQuaternionExtensions.SlerpWithReferenceUp(
  353. stateA.RawOrientation, stateB.RawOrientation, t, blendUp);
  354. }
  355. else
  356. {
  357. // Put the target in the center
  358. newOrient = Quaternion.LookRotation(dirTarget, blendUp);
  359. // Blend the desired offsets from center
  360. Vector2 deltaA = -stateA.RawOrientation.GetCameraRotationToTarget(
  361. stateA.ReferenceLookAt - stateA.CorrectedPosition, blendUp);
  362. Vector2 deltaB = -stateB.RawOrientation.GetCameraRotationToTarget(
  363. stateB.ReferenceLookAt - stateB.CorrectedPosition, blendUp);
  364. newOrient = newOrient.ApplyCameraRotation(
  365. Vector2.Lerp(deltaA, deltaB, adjustedT), blendUp);
  366. }
  367. }
  368. }
  369. state.RawOrientation = ApplyRotBlendHint(
  370. stateA.RawOrientation, stateA.BlendHint,
  371. stateB.RawOrientation, stateB.BlendHint,
  372. state.RawOrientation, newOrient);
  373. // Accumulate the custom blendables and apply the weights
  374. for (int i = 0; i < stateA.NumCustomBlendables; ++i)
  375. {
  376. CustomBlendable b = stateA.GetCustomBlendable(i);
  377. b.m_Weight *= (1-t);
  378. if (b.m_Weight > 0)
  379. state.AddCustomBlendable(b);
  380. }
  381. for (int i = 0; i < stateB.NumCustomBlendables; ++i)
  382. {
  383. CustomBlendable b = stateB.GetCustomBlendable(i);
  384. b.m_Weight *= t;
  385. if (b.m_Weight > 0)
  386. state.AddCustomBlendable(b);
  387. }
  388. return state;
  389. }
  390. static float InterpolateFOV(float fovA, float fovB, float dA, float dB, float t)
  391. {
  392. // We interpolate shot height
  393. float hA = dA * 2f * Mathf.Tan(fovA * Mathf.Deg2Rad / 2f);
  394. float hB = dB * 2f * Mathf.Tan(fovB * Mathf.Deg2Rad / 2f);
  395. float h = Mathf.Lerp(hA, hB, t);
  396. float fov = 179f;
  397. float d = Mathf.Lerp(dA, dB, t);
  398. if (d > UnityVectorExtensions.Epsilon)
  399. fov = 2f * Mathf.Atan(h / (2 * d)) * Mathf.Rad2Deg;
  400. return Mathf.Clamp(fov, Mathf.Min(fovA, fovB), Mathf.Max(fovA, fovB));
  401. }
  402. static Vector3 ApplyPosBlendHint(
  403. Vector3 posA, BlendHintValue hintA,
  404. Vector3 posB, BlendHintValue hintB,
  405. Vector3 original, Vector3 blended)
  406. {
  407. if (((hintA | hintB) & BlendHintValue.NoPosition) == 0)
  408. return blended;
  409. if (((hintA & hintB) & BlendHintValue.NoPosition) != 0)
  410. return original;
  411. if ((hintA & BlendHintValue.NoPosition) != 0)
  412. return posB;
  413. return posA;
  414. }
  415. static Quaternion ApplyRotBlendHint(
  416. Quaternion rotA, BlendHintValue hintA,
  417. Quaternion rotB, BlendHintValue hintB,
  418. Quaternion original, Quaternion blended)
  419. {
  420. if (((hintA | hintB) & BlendHintValue.NoOrientation) == 0)
  421. return blended;
  422. if (((hintA & hintB) & BlendHintValue.NoOrientation) != 0)
  423. return original;
  424. if ((hintA & BlendHintValue.NoOrientation) != 0)
  425. return rotB;
  426. return rotA;
  427. }
  428. Vector3 InterpolatePosition(
  429. Vector3 posA, Vector3 pivotA,
  430. Vector3 posB, Vector3 pivotB,
  431. float t)
  432. {
  433. #pragma warning disable 1718 // comparison made to same variable
  434. if (pivotA == pivotA && pivotB == pivotB) // check for NaN
  435. {
  436. if ((BlendHint & BlendHintValue.CylindricalPositionBlend) != 0)
  437. {
  438. // Cylindrical interpolation about pivot
  439. var a = Vector3.ProjectOnPlane(posA - pivotA, ReferenceUp);
  440. var b = Vector3.ProjectOnPlane(posB - pivotB, ReferenceUp);
  441. var c = Vector3.Slerp(a, b, t);
  442. posA = (posA - a) + c;
  443. posB = (posB - b) + c;
  444. }
  445. else if ((BlendHint & BlendHintValue.SphericalPositionBlend) != 0)
  446. {
  447. // Spherical interpolation about pivot
  448. var c = Vector3.Slerp(posA - pivotA, posB - pivotB, t);
  449. posA = pivotA + c;
  450. posB = pivotB + c;
  451. }
  452. }
  453. return Vector3.Lerp(posA, posB, t);
  454. }
  455. }
  456. }