using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; public class PlanoConvexLens : OpticalSurface2D { public float LensRadius = 1.0f; public float CurveRadius = 1.0f; public int SamplesCount = 10; public float Height = 0.1f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private RaycastMath.RaycastResult2D[] _raycasts; public override bool Raycast(Ray2D ray, ref RaycastMath.RaycastResult2D result) { if(_raycasts == null) { _raycasts = new RaycastMath.RaycastResult2D[4]; } var localSpaceRay = ray; localSpaceRay.origin = transform.InverseTransformPoint(localSpaceRay.origin); localSpaceRay.direction = transform.InverseTransformVector(localSpaceRay.direction).normalized; //cs = as + bs //bs = cs - as float h = Mathf.Sqrt(CurveRadius * CurveRadius - LensRadius * LensRadius); var sphereCastRay = localSpaceRay; sphereCastRay.origin += Vector2.up * (Mathf.Sign(CurveRadius) * h - Height); int raycastsCount = 0; bool curveIntersected = RaycastMath.RaycastCircle(sphereCastRay, Mathf.Abs( CurveRadius), ref _raycasts[raycastsCount]); if (curveIntersected) { _raycasts[raycastsCount].Point += Vector2.down * Mathf.Sign(CurveRadius) * (h); if(CurveRadius >= 0) { if (_raycasts[raycastsCount].Point.y < 0) { curveIntersected = false; } else { _raycasts[raycastsCount].Point += Vector2.up * Height; raycastsCount++; } } else { if (_raycasts[raycastsCount].Point.y > 0) { curveIntersected = false; } else { _raycasts[raycastsCount].Point += Vector2.up * Height; _raycasts[raycastsCount].Normal = -_raycasts[raycastsCount].Normal; raycastsCount++; } } } Vector2 leftHitPoint = Vector2.zero; if(Height > 0.0f && RaycastMath.RayLineSegmentIntersection(localSpaceRay.origin, localSpaceRay.direction, new Vector2(-LensRadius, 0), new Vector2(-LensRadius, Height), ref leftHitPoint)) { _raycasts[raycastsCount].Normal = Vector2.left; _raycasts[raycastsCount].Point = leftHitPoint; raycastsCount++; } Vector2 rightHitPoint = Vector2.zero; if(Height > 0.0f && RaycastMath.RayLineSegmentIntersection(localSpaceRay.origin, localSpaceRay.direction, new Vector2(LensRadius, 0), new Vector2(LensRadius, Height), ref rightHitPoint)) { _raycasts[raycastsCount].Normal = Vector2.right; _raycasts[raycastsCount].Point = rightHitPoint; raycastsCount++; } Vector2 bottomHitPoint = Vector2.zero; if(LensRadius > 0.0f && RaycastMath.RayLineSegmentIntersection(localSpaceRay.origin, localSpaceRay.direction, new Vector2(-LensRadius, 0), new Vector2(LensRadius, 0), ref bottomHitPoint)) { _raycasts[raycastsCount].Normal = Vector2.down; _raycasts[raycastsCount].Point = bottomHitPoint; raycastsCount++; } if(raycastsCount == 0) { return false; } result = _raycasts.Take(raycastsCount).OrderBy(x => Vector2.Distance(x.Point, localSpaceRay.origin)).First(); result.Point = transform.TransformPoint(result.Point); result.Normal = transform.InverseTransformVector(result.Normal).normalized; return true; } private void OnValidate() { if(CurveRadius > 0 && CurveRadius < LensRadius) { CurveRadius = LensRadius; } if (CurveRadius < 0 && CurveRadius > -LensRadius) { CurveRadius = -LensRadius; } var points = RadialCurve(LensRadius, CurveRadius, SamplesCount); if(points.Length < 2) { return; } Shape = new Line[points.Length - 1 + 3]; for(int i = 0; i < points.Length - 1; i++) { Shape[i] = new Line() { P1 = points[i] + Vector2.up * Height, P2 = points[i + 1] + Vector2.up * Height }; } Vector2 p1 = new Vector2(-LensRadius, Height); Vector2 p2 = new Vector2(-LensRadius, 0); Vector2 p3 = new Vector2(LensRadius, 0); Vector2 p4 = new Vector2(LensRadius, Height); Shape[Shape.Length - 3] = new Line() { P1 = p1, P2 = p2 }; Shape[Shape.Length - 2] = new Line() { P1 = p2, P2 = p3 }; Shape[Shape.Length - 1] = new Line() { P1 = p3, P2 = p4 }; } public static Vector2[] RadialCurve(float lenseRadius, float curveRadius, int samplesCount) { //r*r = x*x + h*h //h*h = r*r - x*x Vector2[] result = new Vector2[samplesCount]; float h = Mathf.Sqrt(curveRadius * curveRadius - lenseRadius * lenseRadius); float dx = (-2.0f * lenseRadius) / (samplesCount - 1); for (int i = 0; i < samplesCount; i++) { float x2 = lenseRadius + dx * i; float h2 = Mathf.Sqrt(curveRadius * curveRadius - x2 * x2); result[i] = new Vector2(x2, Mathf.Sign(curveRadius) * (h2 - h)); } return result; } }