Advertisement
walkerbuildapps

cool ball

Apr 14th, 2025
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 27.67 KB | None | 0 0
  1. // === Orb Moving ===
  2. //join r/keisen for more source code, designs, other cool stuff
  3.  
  4. // How to Use:
  5. // 1. Create a new SwiftUI project in Xcode.
  6. // 2. Replace the default ContentView.swift with the ContentView code below.
  7. // 3. Create a new Swift file named ShapeUniforms.swift and paste its code below.
  8. // 4. Create a new Swift file named UIEffectRenderer.swift and paste its code below.
  9. // 5. Create a new Swift file named UIEffectView.swift and paste its code below.
  10. // 6. Create a new Metal file named UIEffectShaders.metal and paste its code below.
  11. // 7. Add an image named exactly "trippywave_texture" to your Assets.xcassets (or change the name in UIEffectRenderer.swift).
  12. // 8. Build and Run! Drag up from the bottom to show sliders.
  13.  
  14.  
  15. //CONTENT VIEW, DO NOT COPY AND PASTE EVERYTHING IN ONE FILE! FOLLOW INSTRUCTIONS ABOVE
  16.  
  17. import SwiftUI
  18. import simd
  19.  
  20. struct ContentView: View {
  21. @State private var warp: Float = 1.8
  22. @State private var speed: Float = 0.1
  23. @State private var blend: Float = 0.3
  24. @State private var warm: Float = 0.6
  25. @State private var noise: Float = 4.0
  26. @State private var contrast: Float = 1.8
  27. @State private var radius: Float = 0.3
  28.  
  29. @State private var panelOffset: CGFloat = UIScreen.main.bounds.height
  30. @State private var startDragOffset: CGFloat = UIScreen.main.bounds.height
  31. @State private var panelHeight: CGFloat = 400
  32.  
  33. private var currentShapeUniforms: ShapeUniforms {
  34. ShapeUniforms(
  35. positionAndRadius: SIMD4<Float>(0, 0, 0, radius),
  36. params1: SIMD4<Float>(blend, warp, speed, warm),
  37. params2: SIMD4<Float>(noise, contrast, 0, 0)
  38. )
  39. }
  40.  
  41. private func formatValue(_ value: Float) -> String {
  42. String(format: "%.2f", value)
  43. }
  44.  
  45. var body: some View {
  46. GeometryReader { screenGeometry in
  47. ZStack(alignment: .bottom) {
  48.  
  49. UIEffectView(shapeUniforms: currentShapeUniforms)
  50. .edgesIgnoringSafeArea(.all)
  51. .gesture(dragGesture(screenGeometry: screenGeometry))
  52.  
  53. parameterPanel(screenGeometry: screenGeometry)
  54. .offset(y: panelOffset)
  55. .background(GeometryReader { panelGeo in
  56. Color.clear.onAppear {
  57. self.panelHeight = panelGeo.size.height
  58. let peekAmount: CGFloat = 50
  59. let maxOffset = max(0, self.panelHeight - peekAmount)
  60. self.panelOffset = maxOffset
  61. self.startDragOffset = self.panelOffset
  62. print("Panel Height: \(self.panelHeight), Initial Offset: \(self.panelOffset)")
  63. }
  64. })
  65.  
  66. }
  67. .edgesIgnoringSafeArea(.bottom)
  68. }
  69. // .statusBar(hidden: true)
  70. }
  71.  
  72. func dragGesture(screenGeometry: GeometryProxy) -> some Gesture {
  73. DragGesture(minimumDistance: 1, coordinateSpace: .global)
  74. .onChanged { value in
  75. let dragAmount = value.translation.height
  76. let targetOffset = startDragOffset + dragAmount
  77. let peekAmount: CGFloat = 50
  78. let maxOffset = max(0, panelHeight - peekAmount)
  79. panelOffset = max(0, min(maxOffset, targetOffset))
  80. }
  81. .onEnded { value in
  82. startDragOffset = panelOffset
  83. let predictedEndOffset = panelOffset + value.predictedEndTranslation.height
  84. let peekAmount: CGFloat = 50
  85. let maxOffset = max(0, panelHeight - peekAmount)
  86. let halfway = maxOffset / 2
  87.  
  88. withAnimation(.interpolatingSpring(stiffness: 250, damping: 25)) {
  89. if predictedEndOffset > halfway {
  90. panelOffset = maxOffset
  91. } else {
  92. panelOffset = 0
  93. }
  94. }
  95. startDragOffset = panelOffset
  96. }
  97. }
  98.  
  99. func parameterPanel(screenGeometry: GeometryProxy) -> some View {
  100. VStack(alignment: .leading, spacing: 10) {
  101. HStack {
  102. Text("Radius:")
  103. .frame(width: 60, alignment: .leading)
  104. Slider(value: $radius, in: 0.1...1.0)
  105. Text(formatValue(radius))
  106. .frame(width: 40, alignment: .trailing)
  107. }
  108. HStack {
  109. Text("Warp:")
  110. .frame(width: 60, alignment: .leading)
  111. Slider(value: $warp, in: 0.0...5.0)
  112. Text(formatValue(warp))
  113. .frame(width: 40, alignment: .trailing)
  114. }
  115. HStack {
  116. Text("Speed:")
  117. .frame(width: 60, alignment: .leading)
  118. Slider(value: $speed, in: 0.0...1.0)
  119. Text(formatValue(speed))
  120. .frame(width: 40, alignment: .trailing)
  121. }
  122. HStack {
  123. Text("Blend:")
  124. .frame(width: 60, alignment: .leading)
  125. Slider(value: $blend, in: 0.0...1.0)
  126. Text(formatValue(blend))
  127. .frame(width: 40, alignment: .trailing)
  128. }
  129. HStack {
  130. Text("Warm:")
  131. .frame(width: 60, alignment: .leading)
  132. Slider(value: $warm, in: 0.0...2.0)
  133. Text(formatValue(warm))
  134. .frame(width: 40, alignment: .trailing)
  135. }
  136. HStack {
  137. Text("Noise:")
  138. .frame(width: 60, alignment: .leading)
  139. Slider(value: $noise, in: 0.1...10.0)
  140. Text(formatValue(noise))
  141. .frame(width: 40, alignment: .trailing)
  142. }
  143. HStack {
  144. Text("Contrast:")
  145. .frame(width: 60, alignment: .leading)
  146. Slider(value: $contrast, in: 0.5...5.0)
  147. Text(formatValue(contrast))
  148. .frame(width: 40, alignment: .trailing)
  149. }
  150.  
  151. RoundedRectangle(cornerRadius: 3)
  152. .fill(Color.gray.opacity(0.5))
  153. .frame(width: 40, height: 6)
  154. .padding(.vertical, 5)
  155. .frame(maxWidth: .infinity, alignment: .center)
  156.  
  157. }
  158. .padding(.horizontal)
  159. .padding(.top, 15)
  160. .padding(.bottom, screenGeometry.safeAreaInsets.bottom + 10)
  161. .background(.regularMaterial)
  162. .cornerRadius(20, corners: [.topLeft, .topRight])
  163. .foregroundColor(.primary)
  164. .font(.system(size: 12))
  165. }
  166. }
  167.  
  168. extension View {
  169. func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
  170. clipShape( RoundedCorner(radius: radius, corners: corners) )
  171. }
  172. }
  173.  
  174. struct RoundedCorner: Shape {
  175. var radius: CGFloat = .infinity
  176. var corners: UIRectCorner = .allCorners
  177.  
  178. func path(in rect: CGRect) -> Path {
  179. let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
  180. return Path(path.cgPath)
  181. }
  182. }
  183.  
  184. struct ContentView_Previews: PreviewProvider {
  185. static var previews: some View {
  186. ContentView()
  187. .preferredColorScheme(.dark)
  188. }
  189. }
  190.  
  191.  
  192.  
  193. //WARNING//
  194. // SEPARATE FILE Shaders.metal
  195. //WARNING//
  196.  
  197. #include <metal_stdlib>
  198. using namespace metal;
  199.  
  200. #define MAX_STEPS 100
  201. #define MAX_DIST 100.0f
  202. #define HIT_THRESHOLD 0.001f
  203. #define NORMAL_EPSILON 0.001f
  204. #define M_PI_F 3.141592653589793f
  205.  
  206. struct VertexOut { float4 position [[position]]; float2 uv; };
  207. struct Uniforms { float time; float2 resolution; };
  208. struct ShapeUniforms { float4 positionAndRadius; float4 params1; float4 params2; };
  209.  
  210. float3 mod289(float3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
  211. float2 mod289(float2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
  212. float3 permute(float3 x) { return mod289(((x*34.0)+1.0)*x); }
  213. float snoise(float2 v) { const float4 C = float4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); float2 i = floor(v + dot(v, C.yy) ); float2 x0 = v - i + dot(i, C.xx); float2 i1 = (x0.x > x0.y) ? float2(1.0f, 0.0f) : float2(0.0f, 1.0f); float2 x1 = x0.xy + C.xx - i1; float2 x2 = x0.xy + C.zz; i = mod289(i); float3 p = permute( permute( i.y + float3(0.0f, i1.y, 1.0f )) + i.x + float3(0.0f, i1.x, 1.0f )); float3 m = max(0.5f - float3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0f); m = m*m; m = m*m; float3 x = 2.0f * fract(p * C.www) - 1.0f; float3 h = abs(x) - 0.5f; float3 ox = floor(x + 0.5f); float3 a0 = x - ox; float3 g; g.x=a0.x*x0.x+h.x*x0.y; g.y=a0.y*x1.x+h.y*x1.y; g.z=a0.z*x2.x+h.z*x2.y; return 130.0f * dot(m, g); }
  214. float fbm_snoise(float2 p, int octaves) { float value = 0.0f; float amplitude = 0.5f; float frequency = 1.0f; for (int i = 0; i < octaves; ++i) { value += amplitude * (snoise(p * frequency) * 0.5f + 0.5f); frequency *= 2.0f; amplitude *= 0.5f; } return clamp(value, 0.0f, 1.0f); }
  215. float3 simpleDomainWarp(float2 uv, float scaled_time, float warpIntensity, int octaves) { float time1 = scaled_time * 1.0f; float time2 = scaled_time * 1.3f; float2 q = float2(fbm_snoise(uv + float2(0.0f, 0.0f) + time1, octaves), fbm_snoise(uv + float2(5.2f, 1.3f) + time2, octaves)); q = q * 2.0f - 1.0f; float2 warped_uv = uv + warpIntensity * q; float final_fbm = fbm_snoise(warped_uv + float2(1.7f, 9.2f) + scaled_time * 0.7f, octaves); return float3(final_fbm, warped_uv.x, warped_uv.y); }
  216.  
  217.  
  218. float sdSphere(float3 p, float3 center, float radius) { return length(p - center) - radius; }
  219. float mapScene(float3 p, constant ShapeUniforms& shapeUniforms) { float3 spherePos = shapeUniforms.positionAndRadius.xyz; float sphereRadius = shapeUniforms.positionAndRadius.w; float sphereDist = sdSphere(p, spherePos, sphereRadius); return sphereDist; }
  220. float3 calcNormal(float3 p, constant ShapeUniforms& shapeUniforms) { float2 e = float2(NORMAL_EPSILON, 0.0); return normalize(float3(mapScene(p + e.xyy, shapeUniforms) - mapScene(p - e.xyy, shapeUniforms), mapScene(p + e.yxy, shapeUniforms) - mapScene(p - e.yxy, shapeUniforms), mapScene(p + e.yyx, shapeUniforms) - mapScene(p - e.yyx, shapeUniforms))); }
  221.  
  222. float calcLighting(float3 pos, float3 normal, float3 lightDir) { float diffuse = max(0.0f, dot(normal, lightDir)); float ambient = 0.2f; return ambient + diffuse * 0.8f; }
  223.  
  224.  
  225. float3 calcBackground(float2 screen_uv, constant Uniforms& uniforms, constant ShapeUniforms& shapeUniforms, texture2d<half, access::sample> tex, sampler samp) {
  226. float aspect = uniforms.resolution.x / uniforms.resolution.y; float textureBlendFactor = shapeUniforms.params1.x; float warpIntensity = 3.0f; float animationSpeed = shapeUniforms.params1.z; float warmColorIntensity = shapeUniforms.params1.w; float noiseScale = shapeUniforms.params2.x; float contrastExponent = shapeUniforms.params2.y; float zoom = noiseScale * 0.8f; float time = (uniforms.time * animationSpeed); float warpTime = time * 0.06f; int octaves = 6; float2 uv_start_raw = (screen_uv - 0.5f) * float2(aspect, 1.0f) * zoom; float driftSpeedX = 0.03f; float driftSpeedY = 0.015f; float2 driftOffset = float2(time * driftSpeedX, time * driftSpeedY); float2 uv_start = uv_start_raw + driftOffset; float3 warp_result = simpleDomainWarp(uv_start, warpTime, warpIntensity, octaves); float f_raw = warp_result.x; float2 warped_uv = warp_result.yz; float f = pow(f_raw, contrastExponent); float3 color_deep_bg=float3(0.01f,0.02f,0.04f); float3 color_mid_cool=float3(0.1f,0.35f,0.5f); float3 color_warm_tint=float3(0.5f,0.3f,0.15f); float3 color_bright_cool=float3(0.7f,0.9f,1.0f); float3 color_highlight=float3(1.0f,1.0f,1.0f); float3 procedural_color; procedural_color=mix(color_deep_bg,color_mid_cool,smoothstep(0.05f,0.55f,f)); float warm_strength = warmColorIntensity; procedural_color=mix(procedural_color,color_warm_tint,smoothstep(0.25f,0.5f,f)*(1.0f-smoothstep(0.45f,0.65f,f))*warm_strength); float bright_cool_strength=0.9f; procedural_color=mix(procedural_color,color_bright_cool,smoothstep(0.55f,0.8f,f)*bright_cool_strength); float highlight_strength=1.0f; procedural_color=mix(procedural_color,color_highlight,smoothstep(0.8f,0.9f,f)*highlight_strength); float texture_scale = 1.0f; float2 warped_uv_normalized = warped_uv / zoom / float2(aspect, 1.0) + 0.5; float2 texture_uv = fract(warped_uv_normalized * texture_scale + screen_uv * texture_scale * 0.3f); half4 texColor = tex.sample(samp, texture_uv); float texture_mix_factor = textureBlendFactor; float3 combined_color = mix(procedural_color, float3(texColor.rgb), texture_mix_factor); combined_color *= (0.95f + f * 0.25f); float vignette_radius=0.7f; float vignette_smoothness=0.6f; float dist_from_center=distance(screen_uv,float2(0.5f)); float vignette=smoothstep(vignette_radius,vignette_radius - vignette_smoothness,dist_from_center); combined_color *= vignette; return combined_color;
  227. }
  228.  
  229.  
  230. vertex VertexOut ui_effect_vertex( uint vertexID [[vertex_id]], constant float4* positions [[buffer(0)]] ) {
  231. VertexOut out; out.position = positions[vertexID];
  232. out.uv = float2(out.position.x * 0.5f + 0.5f, out.position.y * -0.5f + 0.5f); return out;
  233. }
  234.  
  235.  
  236. // This is the main GPU function that runs for every pixel!
  237. fragment half4 ui_effect_fragment(
  238. VertexOut in [[stage_in]],
  239. constant Uniforms& uniforms [[buffer(1)]],
  240. constant ShapeUniforms& shapeUniforms [[buffer(2)]],
  241. texture2d<half, access::sample> bgTexture [[texture(0)]],
  242. sampler bgSampler [[sampler(0)]]
  243. ) {
  244.  
  245. float2 uv = in.uv; float aspect = uniforms.resolution.x / uniforms.resolution.y;
  246. float2 screenPos = (uv * 2.0f - 1.0f) * float2(aspect, 1.0f);
  247. float3 ro = float3(screenPos.x, screenPos.y, 2.0f); float3 rd = float3(0.0f, 0.0f, -1.0f);
  248.  
  249. float total_dist = 0.0f; float3 p = ro; bool hit = false;
  250. for (int i = 0; i < MAX_STEPS; ++i) { float d = mapScene(p, shapeUniforms); if (d < HIT_THRESHOLD) { hit = true; p += rd * d; break; } total_dist += d; p += rd * d; if (total_dist > MAX_DIST) { break; } }
  251.  
  252. // Grab all the parameters sent from the Swift code (sliders etc).
  253. float textureBlendFactor = shapeUniforms.params1.x; float warpIntensity = shapeUniforms.params1.y; float animationSpeed = shapeUniforms.params1.z; float warmColorIntensity = shapeUniforms.params1.w; float noiseScale = shapeUniforms.params2.x; float contrastExponent = shapeUniforms.params2.y;
  254. float scaledTime = uniforms.time * animationSpeed;
  255.  
  256. float3 final_color;
  257.  
  258. // Did our ray marching hit the sphere or just the background?
  259. if (hit) {
  260.  
  261. float3 normal = calcNormal(p, shapeUniforms);
  262. float3 n = normal; float phi = acos(clamp(n.y, -1.0f, 1.0f)); float theta = atan2(n.z, n.x);
  263. float2 sphereUV_raw; sphereUV_raw.x = theta / (2.0f * M_PI_F) + 0.5f; sphereUV_raw.y = phi / M_PI_F;
  264.  
  265. float rotationSpeed = -0.05;
  266. float rotationOffset = scaledTime * rotationSpeed;
  267. float2 sphereUV = sphereUV_raw;
  268. sphereUV.x = sphereUV.x + rotationOffset;
  269.  
  270. float sphereTime = scaledTime * 0.07f; float sphereNoiseScale = noiseScale; float sphereWarpIntensity = warpIntensity; int sphereOctaves = 6;
  271.  
  272. float3 sphereWarpResult = simpleDomainWarp(sphereUV * sphereNoiseScale, sphereTime, sphereWarpIntensity, sphereOctaves);
  273. float baseSphereNoiseRaw = sphereWarpResult.x; float2 warpedSphereUV_raw = sphereWarpResult.yz;
  274. float f = pow(baseSphereNoiseRaw, contrastExponent);
  275. float3 color_deep_bg=float3(0.01f,0.02f,0.04f); float3 color_mid_cool=float3(0.1f,0.35f,0.5f); float3 color_warm_tint=float3(0.5f,0.3f,0.15f); float3 color_bright_cool=float3(0.7f,0.9f,1.0f); float3 color_highlight=float3(1.0f,1.0f,1.0f);
  276. float3 proceduralSphereColor; proceduralSphereColor=mix(color_deep_bg,color_mid_cool,smoothstep(0.05f,0.55f,f)); float warm_strength = warmColorIntensity; proceduralSphereColor=mix(proceduralSphereColor,color_warm_tint,smoothstep(0.25f,0.5f,f)*(1.0f-smoothstep(0.45f,0.65f,f))*warm_strength); float bright_cool_strength=0.9f; proceduralSphereColor=mix(proceduralSphereColor,color_bright_cool,smoothstep(0.55f,0.8f,f)*bright_cool_strength); float highlight_strength=1.0f; proceduralSphereColor=mix(proceduralSphereColor,color_highlight,smoothstep(0.8f,0.9f,f)*highlight_strength);
  277.  
  278. float texture_scale = 1.2f; float2 warpedSphereUV_normalized = warpedSphereUV_raw / sphereNoiseScale;
  279. float2 sphereTextureUV = fract(warpedSphereUV_normalized * texture_scale + sphereUV * texture_scale * 0.3f);
  280. half4 texColor = bgTexture.sample(bgSampler, sphereTextureUV);
  281. float texture_mix_factor = textureBlendFactor; float3 combinedSphereColor = mix(proceduralSphereColor, float3(texColor.rgb), texture_mix_factor);
  282. float3 sphereBaseColor = combinedSphereColor * (1.1f + f * 0.5f);
  283.  
  284. float3 viewDir = -rd; float dotNV = dot(normal, viewDir); float rimPower = 3.5f; float fresnel = pow(1.0f - max(dotNV, 0.0f), rimPower); float3 rimColor = float3(0.9f, 1.0f, 1.1f); float rimIntensity = 0.6f; float3 rimContribution = rimColor * fresnel * rimIntensity;
  285.  
  286. final_color = sphereBaseColor + rimContribution;
  287.  
  288. } else {
  289.  
  290. final_color = calcBackground(in.uv, uniforms, shapeUniforms, bgTexture, bgSampler);
  291. }
  292.  
  293. return half4(half3(clamp(final_color, 0.0f, 1.0f)), 1.0h);
  294. }
  295.  
  296. //WARNING//
  297. // SEPERATE FILE ShapeUniforms.swift
  298. //WARNING//
  299.  
  300. import simd
  301. struct ShapeUniforms: Equatable {
  302. var positionAndRadius: SIMD4<Float>
  303. var params1: SIMD4<Float>
  304. var params2: SIMD4<Float>
  305.  
  306. // adjust default values as needed
  307. init(positionAndRadius: SIMD4<Float> = SIMD4<Float>(0, 0, 0, 0.5),
  308. params1: SIMD4<Float> = SIMD4<Float>(0.5, 1.8, 0.1, 0.6),
  309. params2: SIMD4<Float> = SIMD4<Float>(4.0, 1.8, 0.0, 0.0)) {
  310. self.positionAndRadius = positionAndRadius
  311. self.params1 = params1
  312. self.params2 = params2
  313. }
  314. }
  315.  
  316.  
  317. //WARNING//
  318. // SEPERATE FILE UIEffectRenderer.swift
  319. //WARNING//
  320.  
  321. import MetalKit
  322. import simd
  323.  
  324. struct UIEffectUniforms {
  325. var time: Float
  326. var resolution: SIMD2<Float>
  327. }
  328.  
  329. class UIEffectRenderer: NSObject, MTKViewDelegate {
  330.  
  331. let device: MTLDevice
  332. let commandQueue: MTLCommandQueue
  333. let pipelineState: MTLRenderPipelineState
  334. let vertexBuffer: MTLBuffer
  335.  
  336. var uniforms: UIEffectUniforms
  337. let uniformsBuffer: MTLBuffer
  338.  
  339. private(set) var currentShapeUniforms: ShapeUniforms = ShapeUniforms()
  340. private let shapeUniformsBuffer: MTLBuffer
  341.  
  342. let texture: MTLTexture
  343. let samplerState: MTLSamplerState
  344.  
  345. let startTime: Date
  346.  
  347. init?(metalKitView: MTKView) {
  348. guard let device = MTLCreateSystemDefaultDevice(),
  349. let commandQueue = device.makeCommandQueue() else {
  350. print("UIEffectRenderer Error: Metal is not supported.")
  351. return nil //gotta bail
  352. }
  353. self.device = device
  354. self.commandQueue = commandQueue
  355. self.startTime = Date()
  356.  
  357. metalKitView.device = device
  358. metalKitView.colorPixelFormat = .bgra8Unorm
  359. metalKitView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
  360. metalKitView.isPaused = false
  361. metalKitView.enableSetNeedsDisplay = false
  362.  
  363. do {
  364. let library = try device.makeDefaultLibrary(bundle: .main)
  365. guard let vertexFunction = library.makeFunction(name: "ui_effect_vertex"),
  366. let fragmentFunction = library.makeFunction(name: "ui_effect_fragment") else {
  367. print("UIEffectRenderer Error: Shader function 'ui_effect_vertex' or 'ui_effect_fragment' not found in default library. Check .metal file and function names.")
  368. return nil
  369. }
  370. let pipelineDescriptor = MTLRenderPipelineDescriptor()
  371. pipelineDescriptor.label = "SDF Effect Pipeline"
  372. pipelineDescriptor.vertexFunction = vertexFunction
  373. pipelineDescriptor.fragmentFunction = fragmentFunction
  374. pipelineDescriptor.colorAttachments[0].pixelFormat = metalKitView.colorPixelFormat
  375. self.pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
  376. } catch {
  377. print("UIEffectRenderer Error: Failed to create Metal pipeline state: \(error). Check shader compilation.")
  378. return nil
  379. }
  380.  
  381. let quadVertices: [SIMD4<Float>] = [ .init(-1,-1,0,1), .init(1,-1,0,1), .init(-1,1,0,1), .init(1,-1,0,1), .init(1,1,0,1), .init(-1,1,0,1) ]
  382. guard let createdVertexBuffer = device.makeBuffer(bytes: quadVertices, length: quadVertices.count * MemoryLayout<SIMD4<Float>>.stride, options: .storageModeShared) else { print("UIEffectRenderer Error: Could not create vertex buffer."); return nil }
  383. self.vertexBuffer = createdVertexBuffer
  384. self.vertexBuffer.label = "Quad Vertices"
  385.  
  386. let textureLoader = MTKTextureLoader(device: device)
  387. let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [.origin: MTKTextureLoader.Origin.topLeft, .generateMipmaps: true, .SRGB: false ]
  388. do {
  389. let textureName = "trippywave_texture"
  390. self.texture = try textureLoader.newTexture(name: textureName, scaleFactor: metalKitView.contentScaleFactor, bundle: nil, options: textureLoaderOptions)
  391. self.texture.label = "\(textureName) Texture"
  392. } catch {
  393. print("UIEffectRenderer Error: Failed loading texture '\("trippywave_texture")': \(error). Check Assets.xcassets.")
  394. return nil
  395. }
  396.  
  397. let samplerDescriptor = MTLSamplerDescriptor()
  398. samplerDescriptor.label = "BG Sampler"; samplerDescriptor.minFilter = .linear; samplerDescriptor.magFilter = .linear; samplerDescriptor.mipFilter = .linear; samplerDescriptor.sAddressMode = .repeat; samplerDescriptor.tAddressMode = .repeat
  399. guard let createdSamplerState = device.makeSamplerState(descriptor: samplerDescriptor) else { print("UIEffectRenderer Error: Failed to create sampler state."); return nil }
  400. self.samplerState = createdSamplerState
  401.  
  402. self.uniforms = UIEffectUniforms(time: 0.0, resolution: .zero)
  403. let uniformBufferSize = MemoryLayout<UIEffectUniforms>.stride
  404. guard let createdUniformsBuffer = device.makeBuffer(length: uniformBufferSize, options: .storageModeShared) else { print("UIEffectRenderer Error: Could not create uniforms buffer."); return nil }
  405. self.uniformsBuffer = createdUniformsBuffer
  406. self.uniformsBuffer.label = "Uniforms Buffer (Time/Res)"
  407.  
  408. let shapeUniformsSize = MemoryLayout<ShapeUniforms>.stride
  409. guard let createdShapeBuffer = device.makeBuffer(length: shapeUniformsSize, options: .storageModeShared) else { print("UIEffectRenderer Error: Could not create shape uniforms buffer."); return nil }
  410. self.shapeUniformsBuffer = createdShapeBuffer
  411. self.shapeUniformsBuffer.label = "Shape Uniforms Buffer"
  412. memset(self.shapeUniformsBuffer.contents(), 0, shapeUniformsSize)
  413.  
  414. super.init()
  415. print("UIEffectRenderer initialized successfully.")
  416. }
  417.  
  418. func updateShapeUniforms(_ newUniforms: ShapeUniforms) {
  419. self.currentShapeUniforms = newUniforms
  420. }
  421.  
  422. func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
  423. uniforms.resolution = SIMD2<Float>(Float(size.width), Float(size.height))
  424. print("UIEffectRenderer: Drawable size changed to \(size.width)x\(size.height)")
  425. }
  426.  
  427. func draw(in view: MTKView) {
  428. guard let commandBuffer = commandQueue.makeCommandBuffer(),
  429. let currentDrawable = view.currentDrawable,
  430. let renderPassDescriptor = view.currentRenderPassDescriptor else {
  431. return
  432. }
  433. commandBuffer.label = "Effect Command Buffer"
  434.  
  435. guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
  436. print("UIEffectRenderer Warning: Could not create render command encoder.")
  437. return
  438. }
  439. renderEncoder.label = "Effect Render Encoder"
  440.  
  441. uniforms.time = Float(Date().timeIntervalSince(startTime))
  442. uniformsBuffer.contents().copyMemory(from: &uniforms, byteCount: MemoryLayout<UIEffectUniforms>.stride)
  443.  
  444. var animatedShapeUniforms = currentShapeUniforms
  445. let floatSpeed: Float = 0.25
  446. let floatAmplitude: Float = 0.04
  447. let yOffset = sin(uniforms.time * floatSpeed * 2.0 * Float.pi) * floatAmplitude
  448. animatedShapeUniforms.positionAndRadius.y += yOffset
  449.  
  450. // Slap the latest shape data (with the floaty animation) into the GPU buffer
  451. let shapeDataPtr = shapeUniformsBuffer.contents().bindMemory(to: ShapeUniforms.self, capacity: 1)
  452. shapeDataPtr[0] = animatedShapeUniforms
  453.  
  454. renderEncoder.setRenderPipelineState(pipelineState)
  455. renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
  456. renderEncoder.setFragmentBuffer(uniformsBuffer, offset: 0, index: 1)
  457. renderEncoder.setFragmentBuffer(shapeUniformsBuffer, offset: 0, index: 2) // This sends our shape struct data!
  458. renderEncoder.setFragmentTexture(texture, index: 0)
  459. renderEncoder.setFragmentSamplerState(samplerState, index: 0)
  460.  
  461. renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6) // Draw the actual fullscreen quad.
  462.  
  463. renderEncoder.endEncoding()
  464. commandBuffer.present(currentDrawable)
  465. commandBuffer.commit()
  466. }
  467. }
  468. //WARNING//
  469. // SEPERATE FILE UIEffectView.swift
  470. //WARNING//
  471.  
  472. import SwiftUI
  473. import MetalKit
  474.  
  475. struct UIEffectView: UIViewRepresentable {
  476. // holds all the parameters we get from ContentView for the shader
  477. var shapeUniforms: ShapeUniforms
  478.  
  479. @StateObject private var coordinator = Coordinator()
  480.  
  481. func makeUIView(context: Context) -> MTKView {
  482. print("UIEffectView: makeUIView")
  483. let mtkView = MTKView()
  484.  
  485. mtkView.delegate = context.coordinator
  486. mtkView.enableSetNeedsDisplay = false
  487. mtkView.isPaused = false
  488.  
  489. context.coordinator.setupRenderer(metalKitView: mtkView)
  490.  
  491. guard let renderer = context.coordinator.renderer else {
  492. print("UIEffectView Error: Renderer is nil after setup attempt in makeUIView.")
  493. return mtkView
  494. }
  495.  
  496. mtkView.device = renderer.device
  497. print("UIEffectView: MTKView device configured.")
  498.  
  499. renderer.updateShapeUniforms(shapeUniforms)
  500.  
  501. return mtkView
  502. }
  503.  
  504. func updateUIView(_ uiView: MTKView, context: Context) {
  505. print("UIEffectView: updateUIView")
  506. context.coordinator.renderer?.updateShapeUniforms(shapeUniforms)
  507. }
  508.  
  509. func makeCoordinator() -> Coordinator {
  510. print("UIEffectView: makeCoordinator")
  511. return coordinator
  512. }
  513.  
  514. class Coordinator: NSObject, MTKViewDelegate, ObservableObject {
  515. var renderer: UIEffectRenderer?
  516.  
  517. override init() {
  518. print("Coordinator: init")
  519. super.init()
  520. }
  521.  
  522. func setupRenderer(metalKitView: MTKView) {
  523. print("Coordinator: Setting up renderer...")
  524. self.renderer = UIEffectRenderer(metalKitView: metalKitView)
  525. if self.renderer == nil {
  526. print("Coordinator: Renderer initialization FAILED.")
  527. } else {
  528. print("Coordinator: Renderer initialized successfully.")
  529. }
  530. }
  531.  
  532. func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
  533. renderer?.mtkView(view, drawableSizeWillChange: size)
  534. }
  535.  
  536. func draw(in view: MTKView) {
  537. renderer?.draw(in: view)
  538. }
  539. }
  540.  
  541. }
  542.  
  543.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement