using System; using System.Collections; using System.Collections.Generic; using uc; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; [ExecuteInEditMode] public class FMCRControl : MonoBehaviour { public enum BlendStrategy { CutCubic, Cubic, Akima } [Header("Control")] public bool autoAddToPriority = true; public int priority = 0; [Header("Lens")] [Range(1, 179)] public int fieldOfView = 60; public float nearClipPlane = 0.3f; public float farClipPlane = 1000; [Header("FMCR")] public BlendStrategy blendStrategy = BlendStrategy.CutCubic; public FreeMotionCameraRig FMCR; public ComposerSettings composerSettings; public Transform composerTarget; public TransposerSettings transposerSettings; public Transform transposerTarget; [SerializeField] public static bool showCameraGuide = false; public Vector3 vCamPosition { get { return transposerTargetPosition; } } public Quaternion vCamRotation { get { float fov = fieldOfView * Mathf.Deg2Rad; float z = 0.5f / Mathf.Tan(fov / 2); float v_point = 0.5f; float h_point = 0.5f; float dutch = 0; if (composerSettings != null) { v_point = composerSettings.SoftGuideBottom * 0.5f + composerSettings.SoftGuideTop * 0.5f; h_point = composerSettings.SoftGuideLeft * 0.5f + composerSettings.SoftGuideRight * 0.5f; dutch = composerSettings.Dutch; } float angle_v = Mathf.Atan2(v_point - 0.5f, z) * Mathf.Rad2Deg; float aspect = 1; if (FMCR != null && FMCR.FMCRCamera != null) aspect = FMCR.FMCRCamera.aspect; float angle_h = -Mathf.Atan2(h_point - 0.5f, z / aspect) * Mathf.Rad2Deg; Quaternion rotation = Quaternion.identity; Vector3 lookat = composerTargetPosition - transposerTargetPosition; if (lookat == Vector3.zero) rotation *= transposerTarget.rotation; else rotation *= Quaternion.LookRotation(lookat); rotation *= Quaternion.Euler(angle_v, angle_h, dutch); return rotation; } } public class myReverserClass : IComparer { int IComparer.Compare(int x, int y) { return y - x; } } static SortedList priorityList = new SortedList(new myReverserClass()); static FMCRControl activeControl = null; static public FMCRControl ActiveControl { get { return activeControl; } } public Vector3 composerTargetPosition { get { Vector3 p = Vector3.zero; Transform target = transform; if (composerTarget != null) target = composerTarget; else if (transposerTarget != null) target = transposerTarget; p += target.position; //screen space?? if (composerSettings != null) p += target.TransformVector(composerSettings.TrackedObjectOffset); return p; } } Vector3 transposerTargetPosition { get { Vector3 position = Vector3.zero; Transform target = transform; if (transposerTarget != null) target = transposerTarget; position += target.position; if (transposerSettings != null) { switch (transposerSettings.TransposerOffsetMode) { case TransposerSettings.TransposerOffsetType.LocalSpaceOnTargetAssignment: Debug.LogError("transposerTargetPosition for LocalSpaceOnTargetAssignment is not implemeted."); break; case TransposerSettings.TransposerOffsetType.LocalSpaceLockedUpVector: Debug.LogError("transposerTargetPosition for LocalSpaceLockedUpVector is not implemeted."); break; case TransposerSettings.TransposerOffsetType.LocalSpaceLockedToTarget: position += target.TransformVector(transposerSettings.OffsetFromTarget); break; case TransposerSettings.TransposerOffsetType.WorldSpace: position += transposerSettings.OffsetFromTarget; break; } } return position; } } private void OnEnable() { if (priorityList.ContainsKey(priority)) { priorityList.Remove(priority); } if (autoAddToPriority) priorityList.Add(priority, this); UpdateActiveControl(); } private void OnDisable() { if (priorityList.ContainsKey(priority) && priorityList[priority] == this) { priorityList.Remove(priority); } UpdateActiveControl(); } void UpdateActiveControl() { if (priorityList.Count > 0) { IEnumerator> it = priorityList.GetEnumerator(); it.MoveNext(); activeControl = it.Current.Value; } } // Update is called once per frame void Update() { if (activeControl != this || Application.isPlaying == false) return; if (FMCR != null && FMCR.FMCRCamera != null) { FMCR.FMCRCamera.fieldOfView = fieldOfView; FMCR.FMCRCamera.nearClipPlane = nearClipPlane; FMCR.FMCRCamera.farClipPlane = farClipPlane; } UpdateTransposer(); UpdateComposer(); } Rect softRect = new Rect(0.5f, 0.5f, 0, 0); Rect hardRect = new Rect(0,0,1,1); float fov = 0; float aspect = 1; public float CurrentAspect { get { return aspect; } } float hSpeed = 1; float vSpeed = 1; float dSpeed = 1; float softLeftAngle = 0; float softRightAngle = 0; float softTopAngle = 0; float softBottomAngle = 0; //float hardLeftAngle = 0; //float hardRightAngle = 0; //float hardTopAngle = 0; //float hardBottomAngle = 0; float dutch = 0; void UpdateAngle() { bool needUpdate = false; if (composerSettings != null) { if (softRect != composerSettings.SoftGuideRect || hardRect != composerSettings.HardGuideRect) { softRect = composerSettings.SoftGuideRect; hardRect = composerSettings.HardGuideRect; hSpeed = composerSettings.HorizontalSoftTrackingSpeed; vSpeed = composerSettings.VerticalSoftTrackingSpeed; dSpeed = composerSettings.DutchTrackingSpeed; dutch = composerSettings.Dutch; needUpdate = true; } } if (FMCR != null && FMCR.FMCRCamera != null) { if (fov != FMCR.FMCRCamera.fieldOfView || aspect != FMCR.FMCRCamera.aspect) { fov = FMCR.FMCRCamera.fieldOfView; aspect = FMCR.FMCRCamera.aspect; needUpdate = true; } } if (needUpdate) { float z = 0.5f / Mathf.Tan(fov * Mathf.Deg2Rad / 2); //hardLeftAngle = -Mathf.Atan2(hardRect.xMin - 0.5f, z / aspect) * Mathf.Rad2Deg; //hardRightAngle = -Mathf.Atan2(hardRect.xMax - 0.5f, z / aspect) * Mathf.Rad2Deg; softLeftAngle = -Mathf.Atan2(softRect.xMin - 0.5f, z / aspect) * Mathf.Rad2Deg; softRightAngle = -Mathf.Atan2(softRect.xMax - 0.5f, z / aspect) * Mathf.Rad2Deg; softTopAngle = -Mathf.Atan2(softRect.yMin - 0.5f, z) * Mathf.Rad2Deg; softBottomAngle = -Mathf.Atan2(softRect.yMax - 0.5f, z) * Mathf.Rad2Deg; //hardTopAngle = -Mathf.Atan2(hardRect.yMin - 0.5f, z) * Mathf.Rad2Deg; //hardBottomAngle = -Mathf.Atan2(hardRect.yMax - 0.5f, z) * Mathf.Rad2Deg; } } void UpdateComposer() { if (FMCR == null || FMCR.FMCRCamera == null) return; UpdateAngle(); Vector3 screenPoint = FMCR.FMCRCamera.WorldToViewportPoint(composerTargetPosition); //is this necessary? if (screenPoint.z == 0) return; float hStep = Mathf.Clamp01(Time.deltaTime / hSpeed); float vStep = Mathf.Clamp01(Time.deltaTime / vSpeed); float dStep = Mathf.Clamp01(Time.deltaTime / dSpeed); { float sideways = FMCR.sideways; //float angle_hl = UC.Math.Nearest360(hardLeftAngle, sideways); //float angle_hr = UC.Math.Nearest360(hardRightAngle, sideways); float angle_sl = UC.Math.Nearest360(softLeftAngle, sideways); float angle_sr = UC.Math.Nearest360(softRightAngle, sideways); //if (horizontal > angle_hl) // FMCR.sideways = angle_sl; //else if (horizontal < angle_hr) // FMCR.sideways = angle_sr; //else if (FMCR.sideways > angle_sl) FMCR.sideways = Mathf.Lerp(sideways, angle_sl, hStep); else if (FMCR.sideways < angle_sr) FMCR.sideways = Mathf.Lerp(sideways, angle_sr, hStep); } { float tilt = FMCR.tilt; //float angle_hb = UC.Math.Nearest360(hardBottomAngle, tilt); //float angle_ht = UC.Math.Nearest360(hardTopAngle, tilt); float angle_sb = UC.Math.Nearest360(softBottomAngle, tilt); float angle_st = UC.Math.Nearest360(softTopAngle, tilt); //if (vertical > angle_hb) // FMCR.sideways = angle_sb; //else if (vertical < angle_ht) // FMCR.sideways = angle_st; //else if (FMCR.tilt > angle_sb) FMCR.tilt = Mathf.Lerp(tilt, angle_sb, vStep); else if (FMCR.tilt < angle_st) FMCR.tilt = Mathf.Lerp(tilt, angle_st, vStep); } FMCR.roll = Mathf.Lerp(FMCR.roll, dutch, dStep); } void UpdateTransposer() { if (FMCR == null) return; Vector3 step = Vector3.one; if (transposerSettings != null) { step.x = Mathf.Clamp01(Time.deltaTime / transposerSettings.TrackingSpeeds.x); step.y = Mathf.Clamp01(Time.deltaTime / transposerSettings.TrackingSpeeds.y); step.z = Mathf.Clamp01(Time.deltaTime / transposerSettings.TrackingSpeeds.z); } Vector3 originTransformPosition = FMCR.NodePosition; bool isCutCubic = (blendStrategy == BlendStrategy.CutCubic); FMCR.transform.position = Lerp(FMCR.transform.position, composerTargetPosition, isCutCubic ? Vector3.one : step); Vector3 a = FMCR.transform.InverseTransformPoint(transposerTargetPosition); Vector3 b = FMCR.transform.InverseTransformPoint(originTransformPosition); bool isAkima = (blendStrategy == BlendStrategy.Akima); Vector3 hvdTarget = FMCR.XYZToHVDNearest(Lerp(b, a, isAkima ? Vector3.one : step)); //TODO if (transposerTargetPosition == composerTargetPosition && transposerSettings != null && transposerSettings.OffsetFromTarget == Vector3.zero) { hvdTarget = FMCR.RotationToHVD(transposerTarget.rotation, 0.1f); hvdTarget.z = 0; } FMCR.horizon = Mathf.Lerp(FMCR.horizon, hvdTarget.x, isAkima ? step.x : 1); FMCR.vertical = Mathf.Lerp(FMCR.vertical, hvdTarget.y, isAkima ? step.y : 1); FMCR.distance = Mathf.Lerp(FMCR.distance, hvdTarget.z, isAkima ? step.z : 1); } #if UNITY_EDITOR private void OnGUI() { if (activeControl != this || showCameraGuide == false) return; if (FMCR == null || FMCR.FMCRCamera == null) return; DrawComposerGUI(FMCR.FMCRCamera, FMCR.FMCRCamera.pixelRect); } #endif delegate void DrawRect(float x, float y, float w, float h); delegate void DrawZone(Rect rect); public void DrawComposerGUI(Camera cam, Rect screenRect) { if (cam == null) return; Color hex_color; Color originalColor = GUI.color; string hardColorString = "#C9615C"; string softColorString = "#62B9CF"; string targetColorString = "#FFF82A"; float alpha = 0.3f; DrawRect drawRect = (float x, float y, float w, float h) => { float xx = screenRect.width * x + screenRect.x; float yy = screenRect.height * (1 - y) + screenRect.y; Rect r = new Rect(xx, yy, screenRect.width * w, -screenRect.height * h); GUI.DrawTexture(r, Texture2D.whiteTexture); }; DrawZone drawZone = (Rect rect) => { drawRect(0, 0, rect.xMin, 1); drawRect(rect.xMax, 0, 1 - rect.xMax, 1); drawRect(rect.xMin, 0, rect.xMax - rect.xMin, rect.yMin); drawRect(rect.xMin, rect.yMax, rect.xMax - rect.xMin, 1 - rect.yMax); }; if (ColorUtility.TryParseHtmlString(hardColorString, out hex_color)) { hex_color.a = alpha; GUI.color = hex_color; if (composerSettings != null) drawZone(composerSettings.HardGuideRect); } if (ColorUtility.TryParseHtmlString(softColorString, out hex_color)) { hex_color.a = alpha; GUI.color = hex_color; if(composerSettings != null) drawZone(composerSettings.SoftGuideRect); } if (ColorUtility.TryParseHtmlString(targetColorString, out hex_color)) { Vector3 pt = cam.WorldToViewportPoint(composerTargetPosition); float hSize = screenRect.height * 0.01f; float x = pt.x * screenRect.width + screenRect.x - hSize; float y = (1 - pt.y) * screenRect.height - hSize + screenRect.y; GUI.color = hex_color; GUI.DrawTexture(new Rect(x, y, hSize * 2, hSize * 2), Texture2D.whiteTexture); } GUI.color = originalColor; } static Vector3 Lerp(Vector3 a, Vector3 b, Vector3 t) { return new Vector3(Mathf.Lerp(a.x, b.x, t.x), Mathf.Lerp(a.y, b.y, t.y), Mathf.Lerp(a.z, b.z, t.z)); } [ContextMenu("DrawCameraGuide")] void DrawCameraGuide() { showCameraGuide = true; } [ContextMenu("HideCameraGuide")] void HideCameraGuide() { showCameraGuide = false; } }