public override IEnumerable IntersectionIterator(Ray ray) { double radiusSquared = radius * radius; Vector2 rayOrigin = new Vector2(ray.origin); Vector2 rayDirectionXY = new Vector2(ray.direction); Vector2 rayDirection = rayDirectionXY.GetNormal(); Vector2 thisPosition = Vector2.Zero; Vector2 deltaFromShpereCenterToRayOrigin = rayOrigin - thisPosition; double distanceFromCircleCenterToRayOrigin = Vector2.Dot(deltaFromShpereCenterToRayOrigin, rayDirection); double lengthFromRayOrginToCircleCenterSquared = Vector2.Dot(deltaFromShpereCenterToRayOrigin, deltaFromShpereCenterToRayOrigin); double lengthFromRayOrigintoNearEdgeOfCircleSquared = lengthFromRayOrginToCircleCenterSquared - radiusSquared; double distanceFromCircleCenterToRaySquared = distanceFromCircleCenterToRayOrigin * distanceFromCircleCenterToRayOrigin; double amountCircleCenterToRayIsGreaterThanRayOriginToEdgeSquared = distanceFromCircleCenterToRaySquared - lengthFromRayOrigintoNearEdgeOfCircleSquared; if (amountCircleCenterToRayIsGreaterThanRayOriginToEdgeSquared > 0) { double distanceFromRayOriginToCircleCenter = -distanceFromCircleCenterToRayOrigin; double amountCircleCenterToRayIsGreaterThanRayOriginToEdge = Math.Sqrt(amountCircleCenterToRayIsGreaterThanRayOriginToEdgeSquared); double scaleRatio = ray.direction.Length / rayDirectionXY.Length; if ((ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace) { IntersectInfo info = new IntersectInfo(); info.hitType = IntersectionType.FrontFace; info.closestHitObject = this; double distanceToFrontHit = (distanceFromRayOriginToCircleCenter - amountCircleCenterToRayIsGreaterThanRayOriginToEdge) * scaleRatio; info.distanceToHit = distanceToFrontHit; info.hitPosition = ray.origin + ray.direction * info.distanceToHit; if (info.hitPosition.z > -height / 2 && info.hitPosition.z < height / 2) { info.normalAtHit = new Vector3(info.hitPosition.x, info.hitPosition.y, 0).GetNormal(); yield return info; } } if ((ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace) { IntersectInfo info = new IntersectInfo(); info.hitType = IntersectionType.BackFace; info.closestHitObject = this; double distanceToBackHit = (distanceFromRayOriginToCircleCenter + amountCircleCenterToRayIsGreaterThanRayOriginToEdge) * scaleRatio; info.distanceToHit = distanceToBackHit; info.hitPosition = ray.origin + ray.direction * info.distanceToHit; if (info.hitPosition.z > -height / 2 && info.hitPosition.z < height / 2) { info.normalAtHit = -(new Vector3(info.hitPosition.x, info.hitPosition.y, 0).GetNormal()); yield return info; } } { bool inFrontOfTopFace; double testDistanceToHit = topPlane.GetDistanceToIntersection(ray, out inFrontOfTopFace); Vector3 topHitPosition = ray.origin + ray.direction * testDistanceToHit; if (topHitPosition.x * topHitPosition.x + topHitPosition.y * topHitPosition.y < topRadius * topRadius) { if ((ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace && inFrontOfTopFace) { IntersectInfo topHitInfo = new IntersectInfo(); topHitInfo.hitPosition = topHitPosition; topHitInfo.closestHitObject = this; topHitInfo.hitType = IntersectionType.FrontFace; topHitInfo.normalAtHit = topPlane.planeNormal; topHitInfo.distanceToHit = testDistanceToHit; yield return topHitInfo; } if ((ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace && !inFrontOfTopFace) { IntersectInfo topHitInfo = new IntersectInfo(); topHitInfo.hitPosition = topHitPosition; topHitInfo.closestHitObject = this; topHitInfo.hitType = IntersectionType.BackFace; topHitInfo.normalAtHit = -topPlane.planeNormal; topHitInfo.distanceToHit = testDistanceToHit; yield return topHitInfo; } } } { bool inFrontOfBottomFace; double testDistanceToHit = bottomPlane.GetDistanceToIntersection(ray, out inFrontOfBottomFace); Vector3 bottomHitPosition = ray.origin + ray.direction * testDistanceToHit; if (bottomHitPosition.x * bottomHitPosition.x + bottomHitPosition.y * bottomHitPosition.y < radius * radius) { if ((ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace && inFrontOfBottomFace) { IntersectInfo bottomHitInfo = new IntersectInfo(); bottomHitInfo.hitPosition = bottomHitPosition; bottomHitInfo.closestHitObject = this; bottomHitInfo.hitType = IntersectionType.FrontFace; bottomHitInfo.normalAtHit = bottomPlane.planeNormal; bottomHitInfo.distanceToHit = testDistanceToHit; yield return bottomHitInfo; } if ((ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace && !inFrontOfBottomFace) { IntersectInfo bottomHitInfo = new IntersectInfo(); bottomHitInfo.hitPosition = bottomHitPosition; bottomHitInfo.closestHitObject = this; bottomHitInfo.hitType = IntersectionType.BackFace; bottomHitInfo.normalAtHit = -bottomPlane.planeNormal; bottomHitInfo.distanceToHit = testDistanceToHit; yield return bottomHitInfo; } } } } }
public override IntersectInfo GetClosestIntersection(Ray ray) { double radiusSquared = radius * radius; Vector2 rayOrigin = new Vector2(ray.origin); Vector2 rayDirectionXY = new Vector2(ray.direction); Vector2 rayDirection = rayDirectionXY.GetNormal(); Vector2 thisPosition = Vector2.Zero; Vector2 deltaFromShpereCenterToRayOrigin = rayOrigin - thisPosition; double distanceFromCircleCenterToRayOrigin = Vector2.Dot(deltaFromShpereCenterToRayOrigin, rayDirection); // negative means the Circle is in front of the ray. double lengthFromRayOrginToCircleCenterSquared = Vector2.Dot(deltaFromShpereCenterToRayOrigin, deltaFromShpereCenterToRayOrigin); double lengthFromRayOrigintoNearEdgeOfCircleSquared = lengthFromRayOrginToCircleCenterSquared - radiusSquared; double distanceFromCircleCenterToRaySquared = distanceFromCircleCenterToRayOrigin * distanceFromCircleCenterToRayOrigin; double amountCircleCenterToRayIsGreaterThanRayOriginToEdgeSquared = distanceFromCircleCenterToRaySquared - lengthFromRayOrigintoNearEdgeOfCircleSquared; if (amountCircleCenterToRayIsGreaterThanRayOriginToEdgeSquared > 0) { { bool inFrontOfTop; double testDistanceToHit = topPlane.GetDistanceToIntersection(ray, out inFrontOfTop); bool wantFrontAndInFront = (ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace && inFrontOfTop; bool wantBackAndInBack = (ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace && !inFrontOfTop; if (wantFrontAndInFront || wantBackAndInBack) { Vector3 topHitPosition = ray.origin + ray.direction * testDistanceToHit; if (topHitPosition.x * topHitPosition.x + topHitPosition.y * topHitPosition.y < topRadius * topRadius) { IntersectInfo topHitInfo = new IntersectInfo(); topHitInfo.hitPosition = topHitPosition; topHitInfo.closestHitObject = this; if (ray.intersectionType == IntersectionType.FrontFace) { topHitInfo.hitType = IntersectionType.FrontFace; topHitInfo.normalAtHit = topPlane.planeNormal; } else { topHitInfo.hitType = IntersectionType.BackFace; topHitInfo.normalAtHit = -topPlane.planeNormal; } topHitInfo.distanceToHit = testDistanceToHit; return topHitInfo; } } } { bool inFrontOfBottom; double testDistanceToHit = bottomPlane.GetDistanceToIntersection(ray, out inFrontOfBottom); if (ray.intersectionType == IntersectionType.FrontFace && inFrontOfBottom || ray.intersectionType == IntersectionType.BackFace && !inFrontOfBottom) { Vector3 bottomHitPosition = ray.origin + ray.direction * testDistanceToHit; if (bottomHitPosition.x * bottomHitPosition.x + bottomHitPosition.y * bottomHitPosition.y < radius * radius) { IntersectInfo bottomHitInfo = new IntersectInfo(); bottomHitInfo.hitPosition = bottomHitPosition; bottomHitInfo.closestHitObject = this; if (ray.intersectionType == IntersectionType.FrontFace) { bottomHitInfo.hitType = IntersectionType.FrontFace; bottomHitInfo.normalAtHit = bottomPlane.planeNormal; } else { bottomHitInfo.hitType = IntersectionType.BackFace; bottomHitInfo.normalAtHit = -bottomPlane.planeNormal; } bottomHitInfo.distanceToHit = testDistanceToHit; return bottomHitInfo; } } } IntersectInfo info = new IntersectInfo(); info.closestHitObject = this; info.hitType = IntersectionType.FrontFace; if (ray.isShadowRay) { return info; } double distanceFromRayOriginToCircleCenter = -distanceFromCircleCenterToRayOrigin; double amountCircleCenterToRayIsGreaterThanRayOriginToEdge = Math.Sqrt(amountCircleCenterToRayIsGreaterThanRayOriginToEdgeSquared); double scaleRatio = ray.direction.Length / rayDirectionXY.Length; if (ray.intersectionType == IntersectionType.FrontFace) { double distanceToFrontHit = (distanceFromRayOriginToCircleCenter - amountCircleCenterToRayIsGreaterThanRayOriginToEdge) * scaleRatio; if (distanceToFrontHit > ray.maxDistanceToConsider || distanceToFrontHit < ray.minDistanceToConsider) { return null; } info.distanceToHit = distanceToFrontHit; info.hitPosition = ray.origin + ray.direction * info.distanceToHit; if (info.hitPosition.z < -height / 2 || info.hitPosition.z > height / 2) { return null; } info.normalAtHit = new Vector3(info.hitPosition.x, info.hitPosition.y, 0).GetNormal(); } else if (ray.intersectionType == IntersectionType.BackFace)// check back faces { double distanceToBackHit = (distanceFromRayOriginToCircleCenter + amountCircleCenterToRayIsGreaterThanRayOriginToEdge) * scaleRatio; if (distanceToBackHit > ray.maxDistanceToConsider || distanceToBackHit < ray.minDistanceToConsider) { return null; } info.hitType = IntersectionType.BackFace; info.distanceToHit = distanceToBackHit; info.hitPosition = ray.origin + ray.direction * info.distanceToHit; if (info.hitPosition.z < height / 2 || info.hitPosition.z > height / 2) { return null; } info.normalAtHit = -(new Vector3(info.hitPosition.x, info.hitPosition.y, 0).GetNormal()); } return info; } return null; }