namespace Fusion.Addons.KCC { using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; public static partial class KCCUtility { // PRIVATE MEMBERS private static readonly float[] _sortPriorities = new float[KCC.CACHE_SIZE]; // PUBLIC METHODS public static void ClampLookRotationAngles(ref float pitch, ref float yaw) { pitch = Mathf.Clamp(pitch, -90.0f, 90.0f); while (yaw > 180.0f) { yaw -= 360.0f; } while (yaw < -180.0f) { yaw += 360.0f; } } public static void ClampLookRotationAngles(ref float pitch, ref float yaw, float minPitch, float maxPitch) { if (minPitch < -90.0f) { minPitch = -90.0f; } if (maxPitch > 90.0f) { maxPitch = 90.0f; } if (maxPitch < minPitch) { maxPitch = minPitch; } pitch = Mathf.Clamp(pitch, minPitch, maxPitch); while (yaw > 180.0f) { yaw -= 360.0f; } while (yaw < -180.0f) { yaw += 360.0f; } } public static Vector2 ClampLookRotationAngles(Vector2 lookRotation) { return ClampLookRotationAngles(lookRotation, -90.0f, 90.0f); } public static Vector2 ClampLookRotationAngles(Vector2 lookRotation, float minPitch, float maxPitch) { if (minPitch < -90.0f) { minPitch = -90.0f; } if (maxPitch > 90.0f) { maxPitch = 90.0f; } if (maxPitch < minPitch) { maxPitch = minPitch; } lookRotation.x = Mathf.Clamp(lookRotation.x, minPitch, maxPitch); while (lookRotation.y > 180.0f) { lookRotation.y -= 360.0f; } while (lookRotation.y < -180.0f) { lookRotation.y += 360.0f; } return lookRotation; } public static void GetClampedLookRotationAngles(Quaternion lookRotation, out float pitch, out float yaw) { Vector3 eulerAngles = lookRotation.eulerAngles; if (eulerAngles.x > 180.0f) { eulerAngles.x -= 360.0f; } if (eulerAngles.y > 180.0f) { eulerAngles.y -= 360.0f; } pitch = Mathf.Clamp(eulerAngles.x, -90.0f, 90.0f); yaw = Mathf.Clamp(eulerAngles.y, -180.0f, 180.0f); } public static Vector2 GetClampedEulerLookRotation(Quaternion lookRotation) { Vector2 eulerAngles = lookRotation.eulerAngles; if (eulerAngles.x > 180.0f) { eulerAngles.x -= 360.0f; } if (eulerAngles.y > 180.0f) { eulerAngles.y -= 360.0f; } eulerAngles.x = Mathf.Clamp(eulerAngles.x, -90.0f, 90.0f); eulerAngles.y = Mathf.Clamp(eulerAngles.y, -180.0f, 180.0f); return eulerAngles; } public static Vector2 GetClampedEulerLookRotation(Vector3 direction) { return GetClampedEulerLookRotation(Quaternion.LookRotation(direction)); } public static Vector2 GetClampedEulerLookRotation(Vector2 lookRotation, Vector2 lookRotationDelta, float minPitch, float maxPitch) { return ClampLookRotationAngles(lookRotation + lookRotationDelta, minPitch, maxPitch); } public static Vector2 GetClampedEulerLookRotationDelta(Vector2 lookRotation, Vector2 lookRotationDelta, float minPitch, float maxPitch) { Vector2 clampedlookRotationDelta = lookRotationDelta; lookRotationDelta.x = Mathf.Clamp(lookRotation.x + lookRotationDelta.x, minPitch, maxPitch) - lookRotation.x; return lookRotationDelta; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float InterpolateRange(float from, float to, float min, float max, float alpha) { float range = max - min; if (range <= 0.0f) throw new ArgumentException($"{nameof(max)} must be greater than {nameof(min)}!"); if (from < min) { from = min; } else if (from > max) { from = max; } if (to < min) { to = min; } else if (to > max) { to = max; } if (from == to) return from; float halfRange = range * 0.5f; float interpolatedValue; if (from < to) { float distance = to - from; if (distance <= halfRange) { interpolatedValue = Mathf.Lerp(from, to, alpha); } else { interpolatedValue = Mathf.Lerp(from + range, to, alpha); if (interpolatedValue > max) { interpolatedValue -= range; } } } else { float distance = from - to; if (distance <= halfRange) { interpolatedValue = Mathf.Lerp(from, to, alpha); } else { interpolatedValue = Mathf.Lerp(from - range, to, alpha); if (interpolatedValue <= min) { interpolatedValue += range; } } } return interpolatedValue; } public static void DumpProcessors(KCC kcc, IKCCProcessor[] processors, int count) { if (count <= 0) return; kcc.Log($"Processors ({count})"); for (int i = 0; i < count; ++i) { IKCCProcessor processor = processors[i]; if (processor is Component processorComponent) { kcc.Log(processorComponent.GetType().Name, processorComponent.name); } else if (processor != null) { kcc.Log(processor.GetType().Name); } else { kcc.Log("NULL"); } } } public static void SortProcessors(KCC kcc, IKCCProcessor[] processors, int count) { if (count <= 1) return; bool isSorted = false; float[] priorities = _sortPriorities; int leftIndex; int rightIndex; float leftPriority; float rightPriority; IKCCProcessor leftProcessor; IKCCProcessor rightProcessor; for (int i = 0; i < count; ++i) { priorities[i] = processors[i].GetPriority(kcc); } while (isSorted == false) { isSorted = true; leftIndex = 0; rightIndex = 1; leftPriority = priorities[leftIndex]; while (rightIndex < count) { rightPriority = priorities[rightIndex]; if (leftPriority >= rightPriority) { leftPriority = rightPriority; } else { priorities[leftIndex] = rightPriority; priorities[rightIndex] = leftPriority; leftProcessor = processors[leftIndex]; rightProcessor = processors[rightIndex]; processors[leftIndex] = rightProcessor; processors[rightIndex] = leftProcessor; isSorted = false; } ++leftIndex; ++rightIndex; } } } public static void SortProcessors(KCC kcc, IList processors) where T : class { int count = processors.Count; if (count <= 1) return; bool isSorted = false; float[] priorities = _sortPriorities; int leftIndex; int rightIndex; float leftPriority; float rightPriority; T leftProcessor; T rightProcessor; for (int i = 0; i < count; ++i) { priorities[i] = ((IKCCProcessor)processors[i]).GetPriority(kcc); } while (isSorted == false) { isSorted = true; leftIndex = 0; rightIndex = 1; leftPriority = priorities[leftIndex]; while (rightIndex < count) { rightPriority = priorities[rightIndex]; if (leftPriority >= rightPriority) { leftPriority = rightPriority; } else { priorities[leftIndex] = rightPriority; priorities[rightIndex] = leftPriority; leftProcessor = processors[leftIndex]; rightProcessor = processors[rightIndex]; processors[leftIndex] = rightProcessor; processors[rightIndex] = leftProcessor; isSorted = false; } ++leftIndex; ++rightIndex; } } } public static void SortProcessors(KCC kcc, IKCCProcessor[] processors, int count) where TStageObject : IKCCStage { if (count <= 1) return; bool isSorted = false; float[] priorities = _sortPriorities; int leftIndex; int rightIndex; float leftPriority; float rightPriority; IKCCProcessor leftProcessor; IKCCProcessor rightProcessor; for (int i = 0; i < count; ++i) { priorities[i] = ((IKCCStage)processors[i]).GetPriority(kcc); } while (isSorted == false) { isSorted = true; leftIndex = 0; rightIndex = 1; leftPriority = priorities[leftIndex]; while (rightIndex < count) { rightPriority = priorities[rightIndex]; if (leftPriority >= rightPriority) { leftPriority = rightPriority; } else { priorities[leftIndex] = rightPriority; priorities[rightIndex] = leftPriority; leftProcessor = processors[leftIndex]; rightProcessor = processors[rightIndex]; processors[leftIndex] = rightProcessor; processors[rightIndex] = leftProcessor; isSorted = false; } ++leftIndex; ++rightIndex; } } } public static void SortProcessors(KCC kcc, IKCCProcessor[] processors, int count) where TStage : IKCCStage where TStageObject : IKCCStage { if (count <= 1) return; bool isSorted = false; float[] priorities = _sortPriorities; int leftIndex; int rightIndex; float leftPriority; float rightPriority; IKCCProcessor leftProcessor; IKCCProcessor rightProcessor; for (int i = 0; i < count; ++i) { priorities[i] = ((TStage)processors[i]).GetPriority(kcc); } while (isSorted == false) { isSorted = true; leftIndex = 0; rightIndex = 1; leftPriority = priorities[leftIndex]; while (rightIndex < count) { rightPriority = priorities[rightIndex]; if (leftPriority >= rightPriority) { leftPriority = rightPriority; } else { priorities[leftIndex] = rightPriority; priorities[rightIndex] = leftPriority; leftProcessor = processors[leftIndex]; rightProcessor = processors[rightIndex]; processors[leftIndex] = rightProcessor; processors[rightIndex] = leftProcessor; isSorted = false; } ++leftIndex; ++rightIndex; } } } public static void SortStages(KCC kcc, IList stages) where TStageObject : IKCCStage { int count = stages.Count; if (count <= 1) return; bool isSorted = false; float[] priorities = _sortPriorities; int leftIndex; int rightIndex; float leftPriority; float rightPriority; TStageObject leftStage; TStageObject rightStage; for (int i = 0; i < count; ++i) { priorities[i] = stages[i].GetPriority(kcc); } while (isSorted == false) { isSorted = true; leftIndex = 0; rightIndex = 1; leftPriority = priorities[leftIndex]; while (rightIndex < count) { rightPriority = priorities[rightIndex]; if (leftPriority >= rightPriority) { leftPriority = rightPriority; } else { priorities[leftIndex] = rightPriority; priorities[rightIndex] = leftPriority; leftStage = stages[leftIndex]; rightStage = stages[rightIndex]; stages[leftIndex] = rightStage; stages[rightIndex] = leftStage; isSorted = false; } ++leftIndex; ++rightIndex; } } } public static void SortStages(KCC kcc, IList stages) where TStage : IKCCStage where TStageObject : IKCCStage { int count = stages.Count; if (count <= 1) return; bool isSorted = false; float[] priorities = _sortPriorities; int leftIndex; int rightIndex; float leftPriority; float rightPriority; TStage leftStage; TStage rightStage; for (int i = 0; i < count; ++i) { priorities[i] = stages[i].GetPriority(kcc); } while (isSorted == false) { isSorted = true; leftIndex = 0; rightIndex = 1; leftPriority = priorities[leftIndex]; while (rightIndex < count) { rightPriority = priorities[rightIndex]; if (leftPriority >= rightPriority) { leftPriority = rightPriority; } else { priorities[leftIndex] = rightPriority; priorities[rightIndex] = leftPriority; leftStage = stages[leftIndex]; rightStage = stages[rightIndex]; stages[leftIndex] = rightStage; stages[rightIndex] = leftStage; isSorted = false; } ++leftIndex; ++rightIndex; } } } public static bool AddUniqueProcessor(KCC kcc, IKCCProcessor processor, IKCCProcessor[] processors, ref int processorCount) { if (processorCount >= processors.Length) return false; if (processor == null) return false; if (processor.IsActive(kcc) == false) return false; for (int i = 0; i < processorCount; ++i) { if (ReferenceEquals(processors[i], processor) == true) return false; } processors[processorCount] = processor; ++processorCount; return true; } public static bool ResolveProcessor(UnityEngine.Object unityObject, out IKCCProcessor processor) { processor = unityObject as IKCCProcessor; if (ReferenceEquals(processor, null) == false) return true; GameObject gameObject = unityObject as GameObject; if (ReferenceEquals(gameObject, null) == false) { processor = gameObject.GetComponent(); if (ReferenceEquals(processor, null) == false) return true; } return false; } public static bool ResolveProcessor(UnityEngine.Object unityObject, out IKCCProcessor processor, out GameObject gameObject, out Component component, out ScriptableObject scriptableObject) { processor = unityObject as IKCCProcessor; if (ReferenceEquals(processor, null) == false) { component = processor as Component; if (ReferenceEquals(component, null) == false) { gameObject = component.gameObject; scriptableObject = null; } else { gameObject = null; scriptableObject = processor as ScriptableObject; } return true; } gameObject = unityObject as GameObject; if (ReferenceEquals(gameObject, null) == false) { processor = gameObject.GetComponent(); if (ReferenceEquals(processor, null) == false) { component = processor as Component; if (ReferenceEquals(component, null) == false) { scriptableObject = null; } else { scriptableObject = processor as ScriptableObject; } return true; } } component = null; scriptableObject = null; return false; } [HideInCallstack] public static void LogInfo(SimulationBehaviour behaviour, params object[] messages) { Log(behaviour, behaviour, default, EKCCLogType.Info, messages); } [HideInCallstack] public static void LogWarning(SimulationBehaviour behaviour, params object[] messages) { Log(behaviour, behaviour, default, EKCCLogType.Warning, messages); } [HideInCallstack] public static void LogError(SimulationBehaviour behaviour, params object[] messages) { Log(behaviour, behaviour, default, EKCCLogType.Error, messages); } [HideInCallstack] public static void Log(SimulationBehaviour behaviour, string logGroup, EKCCLogType logType, params object[] messages) { Log(behaviour, behaviour, logGroup, logType, messages); } [HideInCallstack] public static void Log(SimulationBehaviour behaviour, UnityEngine.Object context, string logGroup, EKCCLogType logType, params object[] messages) { System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); NetworkRunner runner = behaviour.Runner; #if UNITY_EDITOR if (Time.frameCount % 2 == 0) { stringBuilder.Append($"[{Time.frameCount}]"); } else { stringBuilder.Append($"[{Time.frameCount}]"); } if (runner != null) { bool isInFixedUpdate = runner.Stage != default; bool isInForwardTick = runner.IsForward == true; if (isInFixedUpdate == true) { if (isInForwardTick == true) { stringBuilder.Append($"[{runner.Tick.Raw}]"); } else { stringBuilder.Append($"[{runner.Tick.Raw}]"); } } else { stringBuilder.Append($"[{runner.Tick.Raw}]"); } } else { stringBuilder.Append($"[--]"); } #else stringBuilder.Append($"[{Time.frameCount}]"); if (runner != null) { bool isInFixedUpdate = runner.Stage != default; bool isInForwardTick = runner.IsForward == true; if (isInFixedUpdate == true) { if (isInForwardTick == true) { stringBuilder.Append($"[FF]"); } else { stringBuilder.Append($"[FR]"); } } else { stringBuilder.Append($"[RF]"); } stringBuilder.Append($"[{runner.Tick.Raw}]"); } else { stringBuilder.Append($"[--]"); stringBuilder.Append($"[--]"); } #endif if (string.IsNullOrEmpty(logGroup) == false) { stringBuilder.Append($"[{logGroup}]"); } stringBuilder.Append($"[{behaviour.name}]"); for (int i = 0; i < messages.Length; ++i) { object message = messages[i]; if (message != null) { stringBuilder.Append($" "); stringBuilder.Append(message); } } switch (logType) { case EKCCLogType.Info: UnityEngine.Debug.Log(stringBuilder.ToString(), context); break; case EKCCLogType.Warning: UnityEngine.Debug.LogWarning(stringBuilder.ToString(), context); break; case EKCCLogType.Error: UnityEngine.Debug.LogError(stringBuilder.ToString(), context); break; default: throw new ArgumentOutOfRangeException(nameof(logType), logType, null); } } [HideInCallstack] [System.Diagnostics.Conditional(KCC.TRACING_SCRIPT_DEFINE)] public static void Trace(SimulationBehaviour behaviour, string logGroup, EKCCLogType logType, params object[] messages) { Log(behaviour, logGroup, logType, messages); } [HideInCallstack] [System.Diagnostics.Conditional(KCC.TRACING_SCRIPT_DEFINE)] public static void Trace(SimulationBehaviour behaviour, params object[] messages) { Log(behaviour, typeof(T).Name, EKCCLogType.Info, messages); } } }