using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Rendering; namespace Assets { public class ARRoute { private const float ProjectionSmoothFrames = 30f; private const float DefaultNearSideOffset = 0.0f; private const float DefaultFarSideOffset = 4f; public int[] LeftSideOffsetFrames; public float[] LeftSideOffsets; public int[] RightSideOffsetFrames; public float[] RightSideOffsets; public bool RearMode; public ARRoute() { } public ARRoute(ARRouteData data) => this.LoadData(data); public int[] Visibility { get; set; } public Vector3[] CameraPositions { get; set; } public Vector3[] FilteredCameraPositions { get; set; } public double[] FrameDistances { get; set; } public Quaternion[] CameraRotations { get; set; } public Quaternion[] CameraRotationsRear { get; set; } public Quaternion[] TrajectoryOrientation { get; set; } public float[] TrajectoryCurvatures { get; set; } public float[] LightRotationFrames { get; set; } public Quaternion[] LightRotations { get; set; } public float[] ShadowIntensityFrames { get; set; } public float[] ShadowIntensities { get; set; } public SphericalHarmonicsL2[] SphericalHarmonics { get; set; } public int[] SlamSegments { get; set; } public CameraProjectionParameters[] ProjectionParameters { get; set; } public bool LeftHanded { get; set; } public int VideoFrameOffset { get; set; } public float CameraHeight { get; set; } public float RiderScale { get; set; } = 1f; public float RouteMargin { get; private set; } = 2f; public bool HasLeftSideOffsets => this.LeftSideOffsetFrames != null && this.LeftSideOffsets != null; public bool HasRightSideOffsets => this.RightSideOffsetFrames != null && this.RightSideOffsets != null; public int FrameCount => this.CameraPositions.Length; public double Lenght => this.FrameDistances[this.FrameCount - 1]; private float DefaultLeftSideOffset => !this.LeftHanded ? 4f : 0.0f; private float DefaultRightSideOffset => !this.LeftHanded ? 0.0f : 4f; public int GetVisibilityAt(int frame) { if (this.Visibility == null || this.Visibility.Length == 0) return 0; if (frame <= 0) return this.Visibility[0]; return frame >= this.Visibility.Length ? this.Visibility[this.Visibility.Length - 1] : this.Visibility[frame]; } public float GetVisibility(float frame) { if (this.Visibility == null || this.Visibility.Length == 0) return 0.0f; int index = (int)Math.Ceiling((double)frame); if (index <= 0) return (float)this.Visibility[0]; if (index >= this.Visibility.Length) return (float)this.Visibility[this.Visibility.Length - 1]; float t = frame - (float)(index - 1); return Mathf.Lerp((float)this.Visibility[index - 1], (float)this.Visibility[index], t); } public Vector3 GetCameraPositionAt(int frame) { if (frame <= 0) return this.CameraPositions[0]; return frame >= this.FrameCount ? this.CameraPositions[this.CameraPositions.Length - 1] : this.CameraPositions[frame]; } public Vector3 GetCameraPosition(float frame) { int index = (int)Math.Ceiling((double)frame); if (index <= 0) return this.CameraPositions[0]; if (index >= this.FrameCount) return this.CameraPositions[this.CameraPositions.Length - 1]; float t = frame - (float)(index - 1); return Vector3.Lerp(this.CameraPositions[index - 1], this.CameraPositions[index], t); } public Vector3 GetFilteredCameraPositionAt(int frame) { if (frame <= 0) return this.FilteredCameraPositions[0]; return frame >= this.FrameCount ? this.FilteredCameraPositions[this.FilteredCameraPositions.Length - 1] : this.FilteredCameraPositions[frame]; } public Vector3 GetFilteredCameraPosition(float frame) { int index = (int)Math.Ceiling((double)frame); if (index <= 0) return this.FilteredCameraPositions[0]; if (index >= this.FrameCount) return this.FilteredCameraPositions[this.FilteredCameraPositions.Length - 1]; float t = frame - (float)(index - 1); return Vector3.Lerp(this.FilteredCameraPositions[index - 1], this.FilteredCameraPositions[index], t); } public Quaternion GetCameraRotationAt(int frame) { if (frame <= 0) return this.CameraRotations[0]; return frame >= this.FrameCount ? this.CameraRotations[this.CameraRotations.Length - 1] : this.CameraRotations[frame]; } public Quaternion GetCameraRotation(float frame) { int index = (int)Math.Ceiling((double)frame); if (index <= 0) return this.CameraRotations[0]; if (index >= this.FrameCount) return this.CameraRotations[this.CameraRotations.Length - 1]; float t = frame - (float)(index - 1); return Quaternion.Slerp(this.CameraRotations[index - 1], this.CameraRotations[index], t); } public Quaternion GetCameraRotationRear(float frame) { int index = (int)Math.Ceiling((double)frame); if (index <= 0) return this.CameraRotationsRear[0]; if (index >= this.FrameCount) return this.CameraRotationsRear[this.CameraRotationsRear.Length - 1]; float t = frame - (float)(index - 1); return Quaternion.Slerp(this.CameraRotationsRear[index - 1], this.CameraRotationsRear[index], t); } public int GetSlamSegmentIndex(float frame) { for (int index = 1; index < this.SlamSegments.Length; ++index) { if ((double)this.SlamSegments[index] > (double)frame) return index - 1; } return this.SlamSegments.Length - 1; } public void GetSlamSegmentRange(int index, out int start, out int end) { index = Mathf.Clamp(index, 0, this.SlamSegments.Length - 1); start = this.SlamSegments[index]; if (index < this.SlamSegments.Length - 1) end = this.SlamSegments[index + 1]; else end = this.FrameCount; } public void GetVisibleSlamSegments( int frame, out int start, out int end, float defultVisibility = 0.0f) { float num = (float)this.GetVisibilityAt(frame); if ((double)num == 0.0) num = defultVisibility; start = this.GetSlamSegmentIndex((float)frame); end = this.GetSlamSegmentIndex((float)frame + num); } public double GetFrameAtDistance(double distance) { double[] frameDistances = this.FrameDistances; int index = Array.BinarySearch(frameDistances, distance); if (index < 0) index = ~index; if (index == 0) return 0.0; if (index == frameDistances.Length) return (double)(frameDistances.Length - 1); double num = VTMath.InverseLerp(frameDistances[index - 1], frameDistances[index], distance); return (double)index + num - 1.0; } public double GetTotalDistance() { return FrameDistances[FrameDistances.Length-1]; } public double GetDistanceForFrame(double frame) { int index = (int)Math.Ceiling(frame); if (index <= 0) return this.FrameDistances[0]; if (index >= this.FrameCount) return this.FrameDistances[this.FrameDistances.Length - 1]; double t = frame - (double)(index - 1); return VTMath.Lerp(this.FrameDistances[index - 1], this.FrameDistances[index], t); } public CameraProjectionParameters GetCameraProjectionParameters( float frame) { int index = Mathf.Clamp(this.GetSlamSegmentIndex(frame), 0, this.ProjectionParameters.Length - 1); CameraProjectionParameters projectionParameter1 = this.ProjectionParameters[index]; int slamSegment1 = this.SlamSegments[index]; if (index > 0 && (double)frame < (double)slamSegment1 + 15.0) { float t = (float)(0.5 + ((double)frame - (double)slamSegment1) / 30.0); return CameraProjectionParameters.Lerp(this.ProjectionParameters[index - 1], projectionParameter1, t); } if (index < this.SlamSegments.Length - 1) { int slamSegment2 = this.SlamSegments[index + 1]; if ((double)frame > (double)slamSegment2 - 15.0) { float t = (float)(0.5 - ((double)slamSegment2 - (double)frame) / 30.0); CameraProjectionParameters projectionParameter2 = this.ProjectionParameters[index + 1]; return CameraProjectionParameters.Lerp(projectionParameter1, projectionParameter2, t); } } return projectionParameter1; } //设置摄像机投影矩阵 public void SetCameraProjection(float frame, Camera camera, bool fitInside = false) { CameraProjectionParameters projectionParameters = this.GetCameraProjectionParameters(frame); if ((double)projectionParameters.alpha == 0.0) return; float num1 = projectionParameters.cx / projectionParameters.cy; float num2 = (float)Screen.width / (float)Screen.height; float cx = projectionParameters.cx; float cy = projectionParameters.cy; if (fitInside) { if ((double)num2 > (double)num1) cx = cy * num2; if ((double)num2 < (double)num1) cy = cx * 1f / num2; } else { if ((double)num2 < (double)num1) cx = cy * num2; if ((double)num2 > (double)num1) cy = cx * 1f / num2; } camera.projectionMatrix = this.GetCameraProjectionMatrix(projectionParameters.alpha, projectionParameters.beta, cx, cy, camera.nearClipPlane, camera.farClipPlane); } private Matrix4x4 GetCameraProjectionMatrix( float alpha, float beta, float cx, float cy, float nearClipPlane, float farClipPlane) { Matrix4x4 projectionMatrix = new Matrix4x4(); float z = (float)(-((double)farClipPlane + (double)nearClipPlane) / ((double)farClipPlane - (double)nearClipPlane)); float w = (float)(-2.0 * (double)farClipPlane * (double)nearClipPlane / ((double)farClipPlane - (double)nearClipPlane)); projectionMatrix.SetRow(0, new Vector4(alpha / cx, 0.0f, 0.0f, 0.0f)); projectionMatrix.SetRow(1, new Vector4(0.0f, beta / cy, 0.0f, 0.0f)); projectionMatrix.SetRow(2, new Vector4(0.0f, 0.0f, z, w)); projectionMatrix.SetRow(3, new Vector4(0.0f, 0.0f, -1f, 0.0f)); return projectionMatrix; } //处理摄像机3d位置 public void CreateFilteredCameraPositions() { int length = this.CameraPositions.Length; Vector3[] vector3Array = new Vector3[length]; for (int index = 0; index < length; ++index) vector3Array[index] = this.CameraPositions[index]; for (int index1 = 0; index1 < 3; ++index1) { for (int index2 = 1; index2 < vector3Array.Length - 1; ++index2) vector3Array[index2] = (vector3Array[index2 - 1] + vector3Array[index2] + vector3Array[index2 + 1]) / 3f; } this.FilteredCameraPositions = vector3Array; } //创建摄像机在3d空间的距离 private void CreateFrameDistances() { int length = this.CameraPositions.Length; this.FrameDistances = new double[length]; this.FrameDistances[0] = 0.0; double num = 0.0; for (int index = 1; index < length; ++index) { num += (double)Vector3.Distance(this.CameraPositions[index - 1], this.CameraPositions[index]); this.FrameDistances[index] = num; } } //创建轨迹上的转向 public void CreateTrajectoryOrientations() { Quaternion[] quaternionArray = new Quaternion[this.FilteredCameraPositions.Length]; for (int index = 1; index < this.FilteredCameraPositions.Length - 1; ++index) quaternionArray[index] = Quaternion.LookRotation(this.FilteredCameraPositions[index + 1] - this.FilteredCameraPositions[index - 1], Vector3.up); quaternionArray[0] = quaternionArray[1]; quaternionArray[this.FilteredCameraPositions.Length - 1] = quaternionArray[this.FilteredCameraPositions.Length - 2]; this.TrajectoryOrientation = quaternionArray; } //获取轨迹上某帧的转向 public Quaternion GetTrajectoryOrientationAt(int frame) { int index = Mathf.Clamp(frame, 0, this.FrameCount); if (index <= 0) return this.TrajectoryOrientation[0]; return index >= this.FrameCount ? this.TrajectoryOrientation[this.TrajectoryOrientation.Length - 1] : this.TrajectoryOrientation[index]; } //鬼区轨迹上模糊帧的转向 public Quaternion GetTrajectoryOrientation(float frame) { frame = Mathf.Clamp(frame, 0.0f, (float)this.FrameCount); int index = (int)Math.Ceiling((double)frame); if (index <= 0) return this.TrajectoryOrientation[0]; if (index >= this.FrameCount) return this.TrajectoryOrientation[this.TrajectoryOrientation.Length - 1]; float t = frame - (float)(index - 1); return Quaternion.Slerp(this.TrajectoryOrientation[index - 1], this.TrajectoryOrientation[index], t); } public float GetTrajectoryCurvature(float frame) { int index = (int)Math.Ceiling((double)frame); if (index <= 0) return this.TrajectoryCurvatures[0]; if (index >= this.FrameCount) return this.TrajectoryCurvatures[this.TrajectoryCurvatures.Length - 1]; float t = frame - (float)(index - 1); return Mathf.Lerp(this.TrajectoryCurvatures[index - 1], this.TrajectoryCurvatures[index], t); } public Quaternion GetLightDirection(float distance) { if (this.LightRotations == null || this.LightRotations.Length == 0) return Quaternion.Euler(new Vector3(90f, 0.0f, 0.0f)); int index1 = 0; for (int index2 = 0; index2 < this.LightRotationFrames.Length; ++index2) { if ((double)this.LightRotationFrames[index2] < (double)distance) index1 = index2; } if (index1 + 1 == this.LightRotations.Length) return this.LightRotations[this.LightRotations.Length - 1]; float t = Mathf.InverseLerp(this.LightRotationFrames[index1], this.LightRotationFrames[index1 + 1], distance); return Quaternion.Slerp(this.LightRotations[index1], this.LightRotations[index1 + 1], t); } public float GetShadowIntensity(float distance) { if (this.ShadowIntensities == null || this.ShadowIntensities.Length == 0) return 1f; int index1 = 0; for (int index2 = 0; index2 < this.ShadowIntensityFrames.Length; ++index2) { if ((double)this.ShadowIntensityFrames[index2] < (double)distance) index1 = index2; } if (index1 + 1 == this.ShadowIntensities.Length) return this.ShadowIntensities[this.ShadowIntensities.Length - 1]; float t = Mathf.InverseLerp(this.ShadowIntensityFrames[index1], this.ShadowIntensityFrames[index1 + 1], distance); return Mathf.Lerp(this.ShadowIntensities[index1], this.ShadowIntensities[index1 + 1], t); } public float GetLeftSideOffset(float frame) { if (!this.HasLeftSideOffsets) return this.DefaultLeftSideOffset; int index = Array.BinarySearch(this.LeftSideOffsetFrames, (int)Math.Ceiling((double)frame)); if (index < 0) index = ~index; if (index == 0) return this.LeftSideOffsets[0]; if (index == this.LeftSideOffsetFrames.Length) return this.LeftSideOffsets[this.LeftSideOffsetFrames.Length - 1]; float t = Mathf.InverseLerp((float)this.LeftSideOffsetFrames[index - 1], (float)this.LeftSideOffsetFrames[index], frame); return Mathf.Lerp(this.LeftSideOffsets[index - 1], this.LeftSideOffsets[index], t); } public float GetRightSideOffset(float frame) { if (!this.HasRightSideOffsets) return this.DefaultRightSideOffset; int index = Array.BinarySearch(this.RightSideOffsetFrames, (int)Math.Ceiling((double)frame)); if (index < 0) index = ~index; if (index == 0) return this.RightSideOffsets[0]; if (index == this.RightSideOffsetFrames.Length) return this.RightSideOffsets[this.RightSideOffsetFrames.Length - 1]; float t = Mathf.InverseLerp((float)this.RightSideOffsetFrames[index - 1], (float)this.RightSideOffsetFrames[index], frame); return Mathf.Lerp(this.RightSideOffsets[index - 1], this.RightSideOffsets[index], t); } public void LoadData(ARRouteData data) { this.Visibility = data.Visibility; this.VideoFrameOffset = data.VideoFrameOffset; this.LeftHanded = data.LeftHanded; this.SlamSegments = data.SlamSegments; this.CameraHeight = data.CameraHeight; this.RiderScale = (double)data.RiderScale != 0.0 ? data.RiderScale : 1f; this.CameraPositions = ((IEnumerable)data.CameraPositions).Select((Func)(x => x.ToUnityVector())).ToArray(); this.ProjectionParameters = data.CameraProjectionParameters; this.CameraRotations = ((IEnumerable)data.CameraRotations).Select((Func)(x => Quaternion.Euler(x.ToUnityVector()))).ToArray(); VectorData[] cameraRotationsRear = data.CameraRotationsRear; this.CameraRotationsRear = cameraRotationsRear != null ? ((IEnumerable)cameraRotationsRear).Select((Func)(x => Quaternion.Euler(x.ToUnityVector()))).ToArray() : (Quaternion[])null; this.LeftSideOffsetFrames = data.LeftSideOffsetFrames; this.LeftSideOffsets = data.LeftSideOffsets; this.RightSideOffsetFrames = data.RightSideOffsetFrames; this.RightSideOffsets = data.RightSideOffsets; if (data.LightRotationFrames == null || data.LightRotationFrames.Length == 0) { this.LightRotationFrames = new float[0]; this.LightRotations = new Quaternion[0]; } else { this.LightRotations = ((IEnumerable)data.LightRotations).Select((Func)(x => Quaternion.Euler(x.ToUnityVector()))).ToArray(); this.LightRotationFrames = data.LightRotationFrames; } if (data.ShadowIntensityFrames == null || data.ShadowIntensityFrames.Length == 0) { this.ShadowIntensityFrames = new float[0]; this.ShadowIntensities = new float[0]; } else { this.ShadowIntensities = data.ShadowIntensities; this.ShadowIntensityFrames = data.ShadowIntensityFrames; } this.SphericalHarmonics = this.LoadSphericalHarmonics(data.SphericalHarmonics); this.CreateFilteredCameraPositions(); this.CreateFrameDistances(); this.CreateTrajectoryOrientations(); this.CreateTrajectoryCurvatures(); } public ARRouteData ToARRouteData() { ARRouteData arRouteData = new ARRouteData(); arRouteData.Visibility = this.Visibility; arRouteData.VideoFrameOffset = this.VideoFrameOffset; arRouteData.LeftHanded = this.LeftHanded; arRouteData.SlamSegments = this.SlamSegments; arRouteData.CameraHeight = this.CameraHeight; arRouteData.RiderScale = (double)this.RiderScale != 0.0 ? this.RiderScale : 1f; arRouteData.CameraPositions = ((IEnumerable)this.CameraPositions).Select((Func)(x => new VectorData(x))).ToArray(); arRouteData.CameraProjectionParameters = this.ProjectionParameters; arRouteData.CameraRotations = ((IEnumerable)this.CameraRotations).Select((Func)(x => new VectorData(x.eulerAngles))).ToArray(); arRouteData.LeftSideOffsetFrames = this.LeftSideOffsetFrames; arRouteData.LeftSideOffsets = this.LeftSideOffsets; arRouteData.RightSideOffsetFrames = this.RightSideOffsetFrames; arRouteData.RightSideOffsets = this.RightSideOffsets; if (this.LightRotationFrames == null || this.LightRotationFrames.Length == 0) { arRouteData.LightRotationFrames = new float[0]; arRouteData.LightRotations = ((IEnumerable)new Quaternion[0]).Select((Func)(x => new VectorData(x.eulerAngles))).ToArray(); } else { arRouteData.LightRotations = ((IEnumerable)this.LightRotations).Select((Func)(x => new VectorData(x.eulerAngles))).ToArray(); arRouteData.LightRotationFrames = this.LightRotationFrames; } if (this.ShadowIntensityFrames == null || this.ShadowIntensityFrames.Length == 0) { arRouteData.ShadowIntensityFrames = new float[1]; arRouteData.ShadowIntensities = new float[1] { 1f }; } else { arRouteData.ShadowIntensities = this.ShadowIntensities; arRouteData.ShadowIntensityFrames = this.ShadowIntensityFrames; } arRouteData.SphericalHarmonics = this.StoreSphericalHarmonics(this.SphericalHarmonics); return arRouteData; } private SphericalHarmonicsL2[] LoadSphericalHarmonics(float[] coefficients) { if (coefficients == null || coefficients.Length == 0) return (SphericalHarmonicsL2[])null; if (coefficients.Length % 27 != 0) throw new ArgumentException("An array of SH coefficients must be divisible by 27."); int num = 0; SphericalHarmonicsL2[] sphericalHarmonicsL2Array = new SphericalHarmonicsL2[coefficients.Length / 27]; for (int index = 0; index < coefficients.Length / 27; ++index) { for (int rgb = 0; rgb < 3; ++rgb) { for (int coefficient = 0; coefficient < 9; ++coefficient) sphericalHarmonicsL2Array[index][rgb, coefficient] = coefficients[num++]; } } return sphericalHarmonicsL2Array; } private float[] StoreSphericalHarmonics(SphericalHarmonicsL2[] sphericalHarmonics) { if (sphericalHarmonics == null || sphericalHarmonics.Length == 0) return (float[])null; int num = 0; float[] numArray = new float[sphericalHarmonics.Length * 27]; for (int index = 0; index < sphericalHarmonics.Length; ++index) { for (int rgb = 0; rgb < 3; ++rgb) { for (int coefficient = 0; coefficient < 9; ++coefficient) numArray[num++] = sphericalHarmonics[index][rgb, coefficient]; } } return numArray; } private void CreateTrajectoryCurvatures() { Vector3[] cameraPositions = this.CameraPositions; double[] frameDistances = this.FrameDistances; int length = cameraPositions.Length; float[] numArray = new float[length]; for (int index = 0; index < length; ++index) { Vector3 aP1 = cameraPositions[index]; double num = frameDistances[index]; Vector3 filteredCameraPosition1 = this.GetFilteredCameraPosition((float)this.GetFrameAtDistance(num - 10.0)); Vector3 filteredCameraPosition2 = this.GetFilteredCameraPosition((float)this.GetFrameAtDistance(num + 10.0)); Vector3 center; if (!Geometry.CircleCenter(filteredCameraPosition1, aP1, filteredCameraPosition2, out center)) { numArray[index] = 0.0f; } else { float magnitude = (center - filteredCameraPosition1).magnitude; Vector3 normalized = Vector3.Cross(aP1 - filteredCameraPosition1, Vector3.up).normalized; if (((double)center.x - (double)filteredCameraPosition1.x) * (double)normalized.x + ((double)center.y - (double)filteredCameraPosition1.y) * (double)normalized.y + ((double)center.z - (double)filteredCameraPosition1.z) * (double)normalized.z < 0.0) magnitude *= -1f; numArray[index] = 1f / magnitude; } } for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 1; index2 < numArray.Length - 1; ++index2) numArray[index2] = (float)(((double)numArray[index2 - 1] + (double)numArray[index2] + (double)numArray[index2 + 1]) / 3.0); } this.TrajectoryCurvatures = numArray; } } }