//
// BlackHole.swift
// GPTBlackHole
//
// Created by JxR on 3/17/23.
//

import Foundation
import SwiftUI
import SceneKit
import Swift
import Metal
import simd

struct LensingUniforms {
 var time: Float
 var radius: Float
 var modelTransform: simd\_float4x4
 var inverseModelTransform: simd\_float4x4
}

struct SceneKitView: UIViewRepresentable {
 var device: MTLDevice!
 var vertexFunction: MTLFunction!
 var fragmentFunction: MTLFunction!
 func addRotationToAccretionDisk(\_ accretionDiskNode: SCNNode) {
 let rotationAction = SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: CGFloat.pi \* 2, z: 0, duration: 10))
 accretionDiskNode.runAction(rotationAction)
 }

 func applyGravitationalLensingShader(to geometry: SCNGeometry) {
 let vertexShaderCode = """
 float radius = 2.5;
 float time = u\_time;

 float angle = atan2(\_geometry.position.x, \_geometry.position.z);
 float radiusFromCenter = length(float2(\_geometry.position.x, \_geometry.position.z));
 float3 newPosition = \_geometry.position.xyz;
 newPosition.y += sin(angle \* 10.0 + time) \* 0.01;

 \_geometry.position.xyz = newPosition;
 """

 let fragmentShaderCode = """
 float3 color = \_surface.diffuse.rgb;
 float angle = atan2(\_surface.position.x, \_surface.position.z);
 float radiusFromCenter = length(float2(\_surface.position.x, \_surface.position.z));

 float whiteFactor = smoothstep(2.45, 2.55, radiusFromCenter);
 float yellowFactor = smoothstep(2.55, 2.65, radiusFromCenter);
 float orangeFactor = smoothstep(2.65, 2.75, radiusFromCenter);

 color = mix(color, float3(1.0, 1.0, 1.0), whiteFactor \* 1.5);
 color = mix(color, float3(1.0, 0.8, 0.2), yellowFactor \* 0.5);
 color = mix(color, float3(1.0, 0.6, 0.1), orangeFactor \* 0.25);

 \_output.color.rgb = color;
 """

 geometry.firstMaterial?.shaderModifiers = [
 .geometry: vertexShaderCode,
 .fragment: fragmentShaderCode
 ]
 }
 func makeUIView(context: Context) -> SCNView {
 let scnView = SCNView()
 let scene = SCNScene()
 scnView.scene = scene
 scnView.autoenablesDefaultLighting = true
 scnView.allowsCameraControl = true
 
 addBlackHoleAndAccretionDisk(to: scene)
 addCamera(to: scene)
 configureSCNView(scnView)

 return scnView
 }

 func updateUIView(\_ uiView: SCNView, context: Context) {}
 
 func addBlackHoleAndAccretionDisk(to scene: SCNScene) {
 // Create black hole sphere
 let blackHoleGeometry = SCNSphere(radius: 1.0)
 let blackHoleMaterial = SCNMaterial()
 blackHoleMaterial.diffuse.contents = UIColor.black
 blackHoleGeometry.materials = [blackHoleMaterial]
 let blackHoleNode = SCNNode(geometry: blackHoleGeometry)
 scene.rootNode.addChildNode(blackHoleNode)

 // Create accretion disk
 let accretionDiskGeometry = SCNTorus(ringRadius: 2.5, pipeRadius: 0.2)
 let accretionDiskMaterial = SCNMaterial()
 accretionDiskMaterial.diffuse.contents = UIColor.red
 accretionDiskGeometry.materials = [accretionDiskMaterial]
 let accretionDiskNode = SCNNode(geometry: accretionDiskGeometry)
 applyGravitationalLensingShader(to: accretionDiskGeometry)
 scene.rootNode.addChildNode(accretionDiskNode)
 addRotationToAccretionDisk(accretionDiskNode)

 }
 func addCamera(to scene: SCNScene) {
 let cameraNode = SCNNode()
 cameraNode.camera = SCNCamera()
 cameraNode.position = SCNVector3(x: -5, y: 3, z: 15)
 cameraNode.look(at: scene.rootNode.position)
 cameraNode.eulerAngles = SCNVector3(x: -.pi / 4, y: 0, z: 0)
 scene.rootNode.addChildNode(cameraNode)
 }
 func configureSCNView(\_ scnView: SCNView) {
 scnView.backgroundColor = UIColor.black
 }
}

So the ring spinning looks really good, but currently the black hole is off screen, and part of the view is white. I want the SceneView to take up the whole screen with the black hole centered for the camera. I also want to simulate the gravitationally lensed rings. Let's first flatten out the ring we are currently using, and make it slightly thinner, and then stack duplicates of itself so that it appears to be a disc of moving rings. Then, we can add spinning rings around the black hole's edges itself, except these rings will appear as a flat spinning disc perpendicular to the camera view at all times.
/
// BlackHole.swift
// GPTBlackHole
//
// 3/17/23에 JxR에 의해 생성되었습니다.
//

import Foundation
import SwiftUI
import SceneKit
import Swift
import Metal
import simd

구조체 LensingUniforms {
 var time: Float
 var radius: Float
 var modelTransform: simd\_float4x4
 var inverseModelTransform: simd\_float4x4
}

구조체 SceneKitView: UIViewRepresentable {
 var device: MTLDevice!
 var vertexFunction: MTLFunction!
 var fragmentFunction: MTLFunction!
 func addRotationToAccretionDisk(\_ accretionDiskNode: SCNNode) {
 let rotationAction = SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: CGFloat.pi \* 2, z: 0, duration: 10))
 accretionDiskNode.runAction(rotationAction)
 }

 func applyGravitationalLensingShader(to 지오메트리: SCNGeometry) {
 let vertexShaderCode = """
 float 반지름 = 2.5;
 float time = u\_time;

 float 각도 = 아탄2(\_지오메트리.위치.x, \_지오메트리.위치.z);
 float radiusFromCenter = length(float2(\_geometry.position.x, \_geometry.position.z));
 float3 newPosition = \_geometry.position.xyz;
 newPosition.y += sin(각도 \* 10.0 + 시간) \* 0.01;

 \_geometry.position.xyz = newPosition;
 """

 let fragmentShaderCode = """
 float3 color = \_surface.diffuse.rgb;
 float 각도 = atan2(\_surface.position.x, \_surface.position.z);
 float radiusFromCenter = length(float2(\_surface.position.x, \_surface.position.z));

 float whiteFactor = smoothstep(2.45, 2.55, radiusFromCenter);
 float yellowFactor = smoothstep(2.55, 2.65, radiusFromCenter);
 float orangeFactor = smoothstep(2.65, 2.75, radiusFromCenter);

 color = mix(color, float3(1.0, 1.0, 1.0), whiteFactor \* 1.5);
 color = mix(color, float3(1.0, 0.8, 0.2), yellowFactor \* 0.5);
 color = mix(color, float3(1.0, 0.6, 0.1), orangeFactor \* 0.25);

 \_output.color.rgb = color;
 """

 geometry.firstMaterial?.shaderModifiers = [
 .지오메트리: 버텍스 셰이더 코드,
 .fragment: 조각 셰이더 코드
 ]
 }
 함수 makeUIView(context: Context) -> SCNView {
 let scnView = SCNView()
 let scene = SCNScene()
 scnView.scene = scene
 scnView.autoenablesDefaultLighting = true
 scnView.allowsCameraControl = true
 
 추가블랙홀및증가디스크(to: 장면)
 추가카메라(to: 장면)
 configureSCNView(scnView)

 반환 scnView
 }

 func updateUIView(\_ uiView: SCNView, context: Context) {}
 
 func addBlackHoleAndAccretionDisk(to scene: SCNScene) {}
 // 블랙홀 구체 생성
 let blackHoleGeometry = SCNSphere(반지름: 1.0)
 let blackHoleMaterial = SCNMaterial()
 blackHoleMaterial.diffuse.contents = UIColor.black
 블랙홀지오메트리.재료 = [블랙홀재료]
 let blackHoleNode = SCNNode(지오메트리: 블랙홀지오메트리)
 scene.rootNode.addChildNode(blackHoleNode)

 // 어커션 디스크 생성
 let accretionDiskGeometry = SCNTorus(ringRadius: 2.5, pipeRadius: 0.2)
 let accretionDiskMaterial = SCNMaterial()
 accretionDiskMaterial.diffuse.contents = UIColor.red
 accretionDiskGeometry.materials = [accretionDiskMaterial]
 let accretionDiskNode = SCNNode(지오메트리: accretionDiskGeometry)
 적용중력렌즈셰이더(대상: 원반 지오메트리)
 scene.rootNode.addChildNode(accretionDiskNode)
 추가 회전 디스크에 추가(추가 디스크 노드)

 }
 함수 addCamera(to scene: SCNScene) {
 let cameraNode = SCNNode()
 cameraNode.camera = SCNCamera()
 cameraNode.position = SCNVector3(x: -5, y: 3, z: 15)
 cameraNode.look(at: scene.rootNode.position)
 cameraNode.eulerAngles = SCNVector3(x: -.pi / 4, y: 0, z: 0)
 scene.rootNode.addChildNode(cameraNode)
 }
 함수 configureSCNView(\_ scnView: SCNView) {
 scnView.backgroundColor = UIColor.black
 }
}

그래서 링이 회전하는 것은 정말 멋지게 보이지만 현재 블랙홀은 화면에서 벗어나 있고 뷰의 일부는 흰색입니다. 블랙홀이 카메라 중앙에 위치하도록 SceneView가 전체 화면을 채우도록 하고 싶습니다. 또한 중력 렌즈를 적용한 고리를 시뮬레이션하고 싶습니다. 먼저 현재 사용 중인 링을 평평하게 만들고 약간 얇게 만든 다음 복제본을 쌓아서 움직이는 링의 원반처럼 보이도록 합니다. 그런 다음 블랙홀의 가장자리 주위에 회전하는 링을 추가할 수 있지만, 이 링은 항상 카메라 뷰에 수직으로 평평하게 회전하는 원반으로 나타납니다.