| | 1 | | using Cysharp.Threading.Tasks; |
| | 2 | | using DCL.Tasks; |
| | 3 | | using System.Collections.Generic; |
| | 4 | | using System.Threading; |
| | 5 | | using UnityEngine; |
| | 6 | | using UnityEngine.Experimental.Rendering; |
| | 7 | |
|
| | 8 | | namespace MainScripts.DCL.Controllers.HUD.CharacterPreview |
| | 9 | | { |
| | 10 | | public class PreviewCameraController : IPreviewCameraController |
| | 11 | | { |
| | 12 | | private const int SUPER_SAMPLING = 1; |
| | 13 | | private const float CAMERA_TRANSITION_TIME = 0.3f; |
| | 14 | |
|
| | 15 | | private Camera camera; |
| | 16 | | private Transform cameraTransform; |
| | 17 | | private Bounds cameraLimits; |
| | 18 | | private (float verticalCenterRef, float bottomMaxOffset, float topMaxOffset) zoomConfig; |
| 0 | 19 | | private CancellationTokenSource cts = new (); |
| | 20 | |
|
| 0 | 21 | | public RenderTexture CurrentTargetTexture => camera.targetTexture; |
| | 22 | |
|
| | 23 | | public void SetCamera(Camera cameraToUse, RenderTexture targetTexture = null) |
| | 24 | | { |
| 0 | 25 | | this.camera = cameraToUse; |
| 0 | 26 | | cameraTransform = this.camera.transform; |
| 0 | 27 | | SetTargetTexture(targetTexture); |
| 0 | 28 | | } |
| | 29 | |
|
| | 30 | | public void SetTargetTexture(RenderTexture targetTexture) => |
| 0 | 31 | | camera.targetTexture = targetTexture; |
| | 32 | |
|
| | 33 | | public void SetCameraEnabled(bool isEnabled) => |
| 0 | 34 | | camera.enabled = isEnabled; |
| | 35 | |
|
| | 36 | | public void SetCameraLimits(Bounds limits) => |
| 0 | 37 | | cameraLimits = limits; |
| | 38 | |
|
| | 39 | | public void ConfigureZoom(float verticalCenterRef, float bottomMaxOffset, float topMaxOffset) |
| | 40 | | { |
| 0 | 41 | | zoomConfig.verticalCenterRef = verticalCenterRef; |
| 0 | 42 | | zoomConfig.bottomMaxOffset = bottomMaxOffset; |
| 0 | 43 | | zoomConfig.topMaxOffset = topMaxOffset; |
| 0 | 44 | | } |
| | 45 | |
|
| | 46 | | public Texture2D TakeSnapshot(int width, int height) |
| | 47 | | { |
| | 48 | | //To enable Post Processing effect add GraphicsFormat.R16G16B16A16_SFloat to the render texture |
| 0 | 49 | | RenderTexture rt = new RenderTexture(width * SUPER_SAMPLING, height * SUPER_SAMPLING, 32); |
| 0 | 50 | | camera.targetTexture = rt; |
| 0 | 51 | | Texture2D screenShot = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false); |
| 0 | 52 | | camera.Render(); |
| 0 | 53 | | RenderTexture.active = rt; |
| 0 | 54 | | screenShot.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); |
| 0 | 55 | | screenShot.Apply(); |
| | 56 | |
|
| 0 | 57 | | return screenShot; |
| | 58 | | } |
| | 59 | |
|
| | 60 | | public void SetFocus(Transform transformToMove, bool useTransition = true) |
| | 61 | | { |
| 0 | 62 | | cts = cts.SafeRestart(); |
| | 63 | |
|
| 0 | 64 | | if (useTransition) |
| | 65 | | { |
| 0 | 66 | | CameraTransitionAsync(cameraTransform.position, transformToMove.position, |
| | 67 | | cameraTransform.rotation, transformToMove.rotation, |
| | 68 | | CAMERA_TRANSITION_TIME, |
| | 69 | | cts.Token).Forget(); |
| | 70 | | } |
| | 71 | | else |
| | 72 | | { |
| 0 | 73 | | cameraTransform.position = transformToMove.position; |
| 0 | 74 | | cameraTransform.rotation = transformToMove.rotation; |
| | 75 | | } |
| 0 | 76 | | } |
| | 77 | |
|
| | 78 | | public void MoveCamera(Vector3 positionDelta, bool changeYLimitsDependingOnZPosition) |
| | 79 | | { |
| 0 | 80 | | cameraTransform.Translate(positionDelta, Space.World); |
| 0 | 81 | | ApplyCameraLimits(changeYLimitsDependingOnZPosition); |
| 0 | 82 | | } |
| | 83 | |
|
| | 84 | | public void Dispose() => |
| 0 | 85 | | cts.SafeCancelAndDispose(); |
| | 86 | |
|
| | 87 | | private async UniTask CameraTransitionAsync( |
| | 88 | | Vector3 initPos, Vector3 endPos, |
| | 89 | | Quaternion initRotation, Quaternion endRotation, |
| | 90 | | float time, CancellationToken ct) |
| | 91 | | { |
| 0 | 92 | | float currentTime = 0; |
| 0 | 93 | | float inverseTime = 1 / time; |
| | 94 | |
|
| 0 | 95 | | while (currentTime < time) |
| | 96 | | { |
| 0 | 97 | | currentTime = Mathf.Clamp(currentTime + Time.deltaTime, 0, time); |
| 0 | 98 | | cameraTransform.position = Vector3.Lerp(initPos, endPos, currentTime * inverseTime); |
| 0 | 99 | | cameraTransform.rotation = Quaternion.Lerp(initRotation, endRotation, currentTime * inverseTime); |
| 0 | 100 | | await UniTask.NextFrame(ct); |
| | 101 | | } |
| 0 | 102 | | } |
| | 103 | |
|
| | 104 | | private void ApplyCameraLimits(bool changeYLimitsDependingOnZPosition) |
| | 105 | | { |
| 0 | 106 | | Vector3 pos = cameraTransform.localPosition; |
| 0 | 107 | | pos.x = Mathf.Clamp(pos.x, cameraLimits.min.x, cameraLimits.max.x); |
| 0 | 108 | | pos.z = Mathf.Clamp(pos.z, cameraLimits.min.z, cameraLimits.max.z); |
| | 109 | |
|
| 0 | 110 | | pos.y = changeYLimitsDependingOnZPosition ? |
| | 111 | | GetCameraYPositionBasedOnZPosition() : |
| | 112 | | Mathf.Clamp(pos.y, cameraLimits.min.y, cameraLimits.max.y); |
| | 113 | |
|
| 0 | 114 | | cameraTransform.localPosition = pos; |
| 0 | 115 | | } |
| | 116 | |
|
| | 117 | | private float GetCameraYPositionBasedOnZPosition() |
| | 118 | | { |
| 0 | 119 | | Vector3 cameraPosition = cameraTransform.localPosition; |
| 0 | 120 | | float minY = zoomConfig.verticalCenterRef - GetOffsetBasedOnZLimits(cameraPosition.z, zoomConfig.bottomMaxOf |
| 0 | 121 | | float maxY = zoomConfig.verticalCenterRef + GetOffsetBasedOnZLimits(cameraPosition.z, zoomConfig.topMaxOffse |
| 0 | 122 | | return Mathf.Clamp(cameraPosition.y, minY, maxY); |
| | 123 | | } |
| | 124 | |
|
| | 125 | | private float GetOffsetBasedOnZLimits(float zValue, float maxOffset) |
| | 126 | | { |
| 0 | 127 | | if (zValue >= cameraLimits.max.z) return 0f; |
| 0 | 128 | | if (zValue <= cameraLimits.min.z) return maxOffset; |
| 0 | 129 | | float progress = (zValue - cameraLimits.min.z) / (cameraLimits.max.z - cameraLimits.min.z); |
| 0 | 130 | | return maxOffset - (progress * maxOffset); |
| | 131 | | } |
| | 132 | | } |
| | 133 | | } |