### MTKPointCloudCoordinator Setup and Drawing Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Manages Metal rendering pipeline and drawing logic for the point cloud. It prepares the pipeline state, depth state, and handles the drawing of point clouds using Metal textures and shaders. ```swift final class MTKPointCloudCoordinator: MTKCoordinator { var staticAngle: Float = 0.0 var staticInc: Float = 0.02 override func preparePipelineAndDepthState() { guard let metalDevice = mtkView.device else { fatalError("Expected a Metal device.") } do { let library = MetalEnvironment.shared.metalLibrary let pipelineDescriptor = MTLRenderPipelineDescriptor() pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm pipelineDescriptor.vertexFunction = library.makeFunction(name: "pointCloudVertexShader") pipelineDescriptor.fragmentFunction = library.makeFunction(name: "pointCloudFragmentShader") pipelineDescriptor.depthAttachmentPixelFormat = .depth32Float pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor) let depthDescriptor = MTLDepthStencilDescriptor() depthDescriptor.isDepthWriteEnabled = true depthDescriptor.depthCompareFunction = .less depthState = metalDevice.makeDepthStencilState(descriptor: depthDescriptor) } catch { print("Unexpected error: \(error).") } } override func draw(in view: MTKView) { guard parent.capturedData.depth != nil else { return } guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else { return } guard let passDescriptor = view.currentRenderPassDescriptor else { return } guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return } encoder.setDepthStencilState(depthState) encoder.setVertexTexture(parent.capturedData.depth, index: 0) encoder.setVertexTexture(parent.capturedData.colorY, index: 1) encoder.setVertexTexture(parent.capturedData.colorCbCr, index: 2) // Scale camera intrinsics to depth resolution let depthResolution = simd_float2( x: Float(parent.capturedData.depth!.width), y: Float(parent.capturedData.depth!.height) ) var cameraIntrinsics = parent.capturedData.cameraIntrinsics let scaleRes = simd_float2( x: Float(parent.capturedData.cameraReferenceDimensions.width) / depthResolution.x, y: Float(parent.capturedData.cameraReferenceDimensions.height) / depthResolution.y ) cameraIntrinsics[0][0] /= scaleRes.x cameraIntrinsics[1][1] /= scaleRes.y cameraIntrinsics[2][0] /= scaleRes.x cameraIntrinsics[2][1] /= scaleRes.y var pmv = calcCurrentPMVMatrix(viewSize: view.frame.size) encoder.setVertexBytes(&pmv, length: MemoryLayout.stride, index: 0) encoder.setVertexBytes(&cameraIntrinsics, length: MemoryLayout.stride, index: 1) encoder.setRenderPipelineState(pipelineState) encoder.drawPrimitives(type: .point, vertexStart: 0, vertexCount: Int(depthResolution.x * depthResolution.y)) encoder.endEncoding() commandBuffer.present(view.currentDrawable!) commandBuffer.commit() } } ``` -------------------------------- ### Initiate Photo Capture with Depth Data Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md Call this method to create photo settings, enable depth data delivery, and start the photo capture process. It checks for available pixel formats to ensure compatibility. ```swift func capturePhoto() { var photoSettings: AVCapturePhotoSettings if photoOutput.availablePhotoPixelFormatTypes.contains(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { photoSettings = AVCapturePhotoSettings(format: [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ]) } else { photoSettings = AVCapturePhotoSettings() } // Capture depth data with this photo capture. photoSettings.isDepthDataDeliveryEnabled = true photoOutput.capturePhoto(with: photoSettings, delegate: self) } ``` -------------------------------- ### Setup LiDAR Depth Capture Session Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Configures an AVCaptureSession to stream synchronized video and LiDAR depth data. Requires specific device and format configurations for optimal performance. Ensure the delegate conforms to CaptureDataReceiver. ```swift import AVFoundation import UIKit // Protocol for receiving captured data protocol CaptureDataReceiver: AnyObject { func onNewData(capturedData: CameraCapturedData) func onNewPhotoData(capturedData: CameraCapturedData) } class CameraController: NSObject, ObservableObject { enum ConfigurationError: Error { case lidarDeviceUnavailable case requiredFormatUnavailable } private let preferredWidthResolution = 1920 private(set) var captureSession: AVCaptureSession! private var photoOutput: AVCapturePhotoOutput! private var depthDataOutput: AVCaptureDepthDataOutput! private var videoDataOutput: AVCaptureVideoDataOutput! private var outputVideoSync: AVCaptureDataOutputSynchronizer! weak var delegate: CaptureDataReceiver? var isFilteringEnabled = true { didSet { depthDataOutput.isFilteringEnabled = isFilteringEnabled } } // Setup the capture session with LiDAR device private func setupCaptureInput() throws { // Look up the LiDAR camera guard let device = AVCaptureDevice.default(.builtInLiDARDepthCamera, for: .video, position: .back) else { throw ConfigurationError.lidarDeviceUnavailable } // Find format with 1920 width, YCbCr pixel format, and depth support guard let format = (device.formats.last { format in format.formatDescription.dimensions.width == preferredWidthResolution && format.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange && !format.isVideoBinned && !format.supportedDepthDataFormats.isEmpty }) else { throw ConfigurationError.requiredFormatUnavailable } // Find depth format with Float16 depth values guard let depthFormat = (format.supportedDepthDataFormats.last { depthFormat in depthFormat.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_DepthFloat16 }) else { throw ConfigurationError.requiredFormatUnavailable } try device.lockForConfiguration() device.activeFormat = format device.activeDepthDataFormat = depthFormat device.unlockForConfiguration() let deviceInput = try AVCaptureDeviceInput(device: device) captureSession.addInput(deviceInput) } func startStream() { captureSession.startRunning() } func stopStream() { captureSession.stopRunning() } func capturePhoto() { var photoSettings: AVCapturePhotoSettings if photoOutput.availablePhotoPixelFormatTypes.contains(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { photoSettings = AVCapturePhotoSettings(format: [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ]) } else { photoSettings = AVCapturePhotoSettings() } photoSettings.isDepthDataDeliveryEnabled = true photoOutput.capturePhoto(with: photoSettings, delegate: self) } } ``` -------------------------------- ### Manage Metal GPU Resources with MetalEnvironment Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Provides a singleton for accessing the Metal device, command queue, and library. Ensures consistent GPU resource initialization across the application. ```swift import Foundation import Metal class MetalEnvironment { static let shared: MetalEnvironment = { MetalEnvironment() }() let metalDevice: MTLDevice let metalCommandQueue: MTLCommandQueue let metalLibrary: MTLLibrary private init() { guard let metalDevice = MTLCreateSystemDefaultDevice() else { fatalError("Unable to create the metal device.") } guard let metalCommandQueue = metalDevice.makeCommandQueue() else { fatalError("Unable to create the command queue.") } guard let metalLibrary = metalDevice.makeDefaultLibrary() else { fatalError("Unable to create the default library.") } self.metalDevice = metalDevice self.metalCommandQueue = metalCommandQueue self.metalLibrary = metalLibrary } } // Example usage in a view coordinator let device = MetalEnvironment.shared.metalDevice let commandQueue = MetalEnvironment.shared.metalCommandQueue let library = MetalEnvironment.shared.metalLibrary let vertexFunction = library.makeFunction(name: "pointCloudVertexShader") let fragmentFunction = library.makeFunction(name: "pointCloudFragmentShader") ``` -------------------------------- ### Configure Device and Depth Formats Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md Select appropriate video and depth formats that match the application's requirements, ensuring the device is locked during configuration. ```swift // Find a match that outputs video data in the format the app's custom Metal views require. guard let format = (device.formats.last { format in format.formatDescription.dimensions.width == preferredWidthResolution && format.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange && !format.isVideoBinned && !format.supportedDepthDataFormats.isEmpty }) else { throw ConfigurationError.requiredFormatUnavailable } // Find a match that outputs depth data in the format the app's custom Metal views require. guard let depthFormat = (format.supportedDepthDataFormats.last { depthFormat in depthFormat.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_DepthFloat16 }) else { throw ConfigurationError.requiredFormatUnavailable } // Begin the device configuration. try device.lockForConfiguration() // Configure the device and depth formats. device.activeFormat = format device.activeDepthDataFormat = depthFormat // Finish the device configuration. device.unlockForConfiguration() ``` -------------------------------- ### Configure Video and Depth Data Outputs Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md Create and add AVCaptureVideoDataOutput and AVCaptureDepthDataOutput to the capture session. Use AVCaptureDataOutputSynchronizer to synchronize data delivery from both outputs. ```swift // Create an object to output video sample buffers. videoDataOutput = AVCaptureVideoDataOutput() captureSession.addOutput(videoDataOutput) // Create an object to output depth data. depthDataOutput = AVCaptureDepthDataOutput() depthDataOutput.isFilteringEnabled = isFilteringEnabled captureSession.addOutput(depthDataOutput) // Create an object to synchronize the delivery of depth and video data. outputVideoSync = AVCaptureDataOutputSynchronizer(dataOutputs: [depthDataOutput, videoDataOutput]) outputVideoSync.setDelegate(self, queue: videoQueue) ``` -------------------------------- ### Configure Photo Output for High-Quality Capture Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md Create an AVCapturePhotoOutput, optimize it for quality, and add it to the capture session. Enable depth data delivery after adding the output to ensure proper pipeline configuration. ```swift // Create an object to output photos. photoOutput = AVCapturePhotoOutput() photoOutput.maxPhotoQualityPrioritization = .quality captureSession.addOutput(photoOutput) // Enable delivery of depth data after adding the output to the capture session. photoOutput.isDepthDataDeliveryEnabled = true ``` -------------------------------- ### Implement ContentView for Depth Visualization Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt This view integrates CameraManager state with depth filtering sliders and multiple Metal-based visualization components. ```swift import SwiftUI import MetalKit struct ContentView: View { @StateObject private var manager = CameraManager() @State private var maxDepth = Float(5.0) @State private var minDepth = Float(0.0) @State private var scaleMovement = Float(1.0) let maxRangeDepth = Float(15) let minRangeDepth = Float(0) var body: some View { VStack { HStack { Button { manager.processingCapturedResult ? manager.resumeStream() : manager.startPhotoCapture() } label: { Image(systemName: manager.processingCapturedResult ? "play.circle" : "camera.circle") .font(.largeTitle) } Text("Depth Filtering") Toggle("Depth Filtering", isOn: $manager.isFilteringDepth).labelsHidden() Spacer() } SliderDepthBoundaryView(val: $maxDepth, label: "Max Depth", minVal: minRangeDepth, maxVal: maxRangeDepth) SliderDepthBoundaryView(val: $minDepth, label: "Min Depth", minVal: minRangeDepth, maxVal: maxRangeDepth) ScrollView { LazyVGrid(columns: [GridItem(.flexible(maximum: 600)), GridItem(.flexible(maximum: 600))]) { if manager.dataAvailable { // Color threshold depth view ZoomOnTap { MetalTextureColorThresholdDepthView( rotationAngle: rotationAngle, maxDepth: $maxDepth, minDepth: $minDepth, capturedData: manager.capturedData ) } // 3D Point cloud view ZoomOnTap { MetalPointCloudView( rotationAngle: rotationAngle, maxDepth: $maxDepth, minDepth: $minDepth, scaleMovement: $scaleMovement, capturedData: manager.capturedData ) } // Depth overlay on color ZoomOnTap { DepthOverlay(manager: manager, maxDepth: $maxDepth, minDepth: $minDepth) } } } } } } } ``` -------------------------------- ### Wrap Depth and Calibration Data into JSON Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Serializes depth data and camera calibration information into a JSON Data object. This function requires helper functions `convertDepthData` and `convertLensDistortionLookupTable` to be defined. ```swift // Wrap depth and calibration data into JSON format func wrapEstimateImageData(depthMap: CVPixelBuffer, calibration: AVCameraCalibrationData) -> Data { let jsonDict: [String : Any] = [ "calibration_data" : [ "intrinsic_matrix" : (0 ..< 3).map{ x in (0 ..< 3).map{ y in calibration.intrinsicMatrix[x][y]} }, "pixel_size" : calibration.pixelSize, "intrinsic_matrix_reference_dimensions" : [ calibration.intrinsicMatrixReferenceDimensions.width, calibration.intrinsicMatrixReferenceDimensions.height ], "lens_distortion_center" : [ calibration.lensDistortionCenter.x, calibration.lensDistortionCenter.y ], "lens_distortion_lookup_table" : convertLensDistortionLookupTable( lookupTable: calibration.lensDistortionLookupTable! ), "inverse_lens_distortion_lookup_table" : convertLensDistortionLookupTable( lookupTable: calibration.inverseLensDistortionLookupTable! ) ], "depth_data" : convertDepthData(depthMap: depthMap) ] let jsonStringData = try! JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted) return jsonStringData } ``` -------------------------------- ### Retrieve the LiDAR Camera Device Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md Use the builtInLiDARDepthCamera device type to access the LiDAR sensor on compatible hardware. ```swift // Look up the LiDAR camera. guard let device = AVCaptureDevice.default(.builtInLiDARDepthCamera, for: .video, position: .back) else { throw ConfigurationError.lidarDeviceUnavailable } ``` -------------------------------- ### CVPixelBuffer Extension - Texture Conversion Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Provides an extension to CVPixelBuffer for creating Metal textures from depth and color data, optimized for GPU rendering. ```APIDOC ## CVPixelBuffer Extension - Texture Conversion ### Description Extension to convert `CVPixelBuffer` depth and color data to Metal textures for GPU rendering. ### Method `texture(withFormat:planeIndex:addToCache:)` ### Endpoint N/A (Swift Extension) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```swift let data = CameraCapturedData( depth: syncedDepthData.depthData.depthDataMap.texture(withFormat: .r16Float, planeIndex: 0, addToCache: textureCache), colorY: pixelBuffer.texture(withFormat: .r8Unorm, planeIndex: 0, addToCache: textureCache), colorCbCr: pixelBuffer.texture(withFormat: .rg8Unorm, planeIndex: 1, addToCache: textureCache), cameraIntrinsics: cameraCalibrationData.intrinsicMatrix, cameraReferenceDimensions: cameraCalibrationData.intrinsicMatrixReferenceDimensions ) ``` ### Response #### Success Response (200) `MTLTexture?` - A Metal texture object if the conversion is successful, otherwise `nil`. #### Response Example ``` // Returns an MTLTexture object or nil ``` ``` -------------------------------- ### Convert CVPixelBuffer to MTLTexture Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Extends CVPixelBuffer to create a Metal texture from a specific plane. Requires a valid CVMetalTextureCache and appropriate pixel format. ```swift import Foundation import AVFoundation extension CVPixelBuffer { func texture(withFormat pixelFormat: MTLPixelFormat, planeIndex: Int, addToCache cache: CVMetalTextureCache) -> MTLTexture? { let width = CVPixelBufferGetWidthOfPlane(self, planeIndex) let height = CVPixelBufferGetHeightOfPlane(self, planeIndex) var cvtexture: CVMetalTexture? CVMetalTextureCacheCreateTextureFromImage(nil, cache, self, nil, pixelFormat, width, height, planeIndex, &cvtexture) guard let texture = cvtexture else { return nil } return CVMetalTextureGetTexture(texture) } } // Usage in capture callback let data = CameraCapturedData( depth: syncedDepthData.depthData.depthDataMap.texture(withFormat: .r16Float, planeIndex: 0, addToCache: textureCache), colorY: pixelBuffer.texture(withFormat: .r8Unorm, planeIndex: 0, addToCache: textureCache), colorCbCr: pixelBuffer.texture(withFormat: .rg8Unorm, planeIndex: 1, addToCache: textureCache), cameraIntrinsics: cameraCalibrationData.intrinsicMatrix, cameraReferenceDimensions: cameraCalibrationData.intrinsicMatrixReferenceDimensions ) ``` -------------------------------- ### Process Captured Photo and Depth Data Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md This delegate method is called upon completion of a photo capture. It retrieves image and depth data, stops the stream, converts depth data, and packages it for the UI layer. Ensure `stopStream()` and `delegate?.onNewPhotoData()` are implemented. ```swift func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { // Retrieve the image and depth data. guard let pixelBuffer = photo.pixelBuffer, let depthData = photo.depthData, let cameraCalibrationData = depthData.cameraCalibrationData else { return } // Stop the stream until the user returns to streaming mode. stopStream() // Convert the depth data to the expected format. let convertedDepth = depthData.converting(toDepthDataType: kCVPixelFormatType_DepthFloat16) // Package the captured data. let data = CameraCapturedData(depth: convertedDepth.depthDataMap.texture(withFormat: .r16Float, planeIndex: 0, addToCache: textureCache), colorY: pixelBuffer.texture(withFormat: .r8Unorm, planeIndex: 0, addToCache: textureCache), colorCbCr: pixelBuffer.texture(withFormat: .rg8Unorm, planeIndex: 1, addToCache: textureCache), cameraIntrinsics: cameraCalibrationData.intrinsicMatrix, cameraReferenceDimensions: cameraCalibrationData.intrinsicMatrixReferenceDimensions) delegate?.onNewPhotoData(capturedData: data) } ``` -------------------------------- ### Point Cloud Vertex Shader with Depth and Color Projection Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt A Metal vertex shader that processes depth and color textures to generate a point cloud. It calculates world coordinates from depth, projects them into clip space, and converts YUV color data to RGB. Requires view matrix and camera intrinsics. ```metal // Vertex output for point cloud rendering typedef struct { float4 clipSpacePosition [[position]]; float2 coor; float pSize [[point_size]]; float depth; half4 color; } ParticleVertexInOut; // Point cloud vertex shader - projects 3D points using camera intrinsics vertex ParticleVertexInOut pointCloudVertexShader( uint vertexID [[ vertex_id ]], texture2d depthTexture [[ texture(0) ]], constant float4x4& viewMatrix [[ buffer(0) ]], constant float3x3& cameraIntrinsics [[ buffer(1) ]], texture2d colorYtexture [[ texture(1) ]], texture2d colorCbCrtexture [[ texture(2) ]]) { ParticleVertexInOut out; uint2 pos; pos.y = vertexID / depthTexture.get_width(); pos.x = vertexID % depthTexture.get_width(); // Get depth in millimeters float depth = (depthTexture.read(pos).x) * 1000.0f; // Calculate world coordinates using camera intrinsics float xrw = ((int)pos.x - cameraIntrinsics[2][0]) * depth / cameraIntrinsics[0][0]; float yrw = ((int)pos.y - cameraIntrinsics[2][1]) * depth / cameraIntrinsics[1][1]; float4 xyzw = { xrw, yrw, depth, 1.f }; // Project to clip space out.clipSpacePosition = viewMatrix * xyzw; // Sample color from YCbCr textures constexpr sampler textureSampler(mag_filter::linear, min_filter::linear); out.coor = { pos.x / (depthTexture.get_width() - 1.0f), pos.y / (depthTexture.get_height() - 1.0f) }; half y = colorYtexture.sample(textureSampler, out.coor).r; half2 uv = colorCbCrtexture.sample(textureSampler, out.coor).rg - half2(0.5h, 0.5h); // Convert YUV to RGB out.color = half4(y + 1.402h * uv.y, y - 0.7141h * uv.y - 0.3441h * uv.x, y + 1.772h * uv.x, 1.0h); out.depth = depth; out.pSize = 5.0f; return out; } ``` -------------------------------- ### MetalPointCloudView SwiftUI Structure Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Defines the SwiftUI view for rendering a 3D point cloud. It conforms to UIViewRepresentable and MetalRepresentable protocols, accepting rotation, depth, and scale parameters, along with captured camera data. ```swift import SwiftUI import MetalKit import Metal struct MetalPointCloudView: UIViewRepresentable, MetalRepresentable { var rotationAngle: Double @Binding var maxDepth: Float @Binding var minDepth: Float @Binding var scaleMovement: Float var capturedData: CameraCapturedData func makeCoordinator() -> MTKPointCloudCoordinator { MTKPointCloudCoordinator(parent: self) } } ``` -------------------------------- ### Handle Synchronized Video and Depth Data Delivery Source: https://github.com/sakutama-11/lidardepthcapture/blob/main/README.md Implement the dataOutputSynchronizer delegate method to retrieve and process synchronized depth and video data. This method is called when new synchronized data is available. ```swift func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer, didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection) { // Retrieve the synchronized depth and sample buffer container objects. guard let syncedDepthData = synchronizedDataCollection.synchronizedData(for: depthDataOutput) as? AVCaptureSynchronizedDepthData, let syncedVideoData = synchronizedDataCollection.synchronizedData(for: videoDataOutput) as? AVCaptureSynchronizedSampleBufferData else { return } guard let pixelBuffer = syncedVideoData.sampleBuffer.imageBuffer, let cameraCalibrationData = syncedDepthData.depthData.cameraCalibrationData else { return } // Package the captured data. let data = CameraCapturedData(depth: syncedDepthData.depthData.depthDataMap.texture(withFormat: .r16Float, planeIndex: 0, addToCache: textureCache), colorY: pixelBuffer.texture(withFormat: .r8Unorm, planeIndex: 0, addToCache: textureCache), colorCbCr: pixelBuffer.texture(withFormat: .rg8Unorm, planeIndex: 1, addToCache: textureCache), cameraIntrinsics: cameraCalibrationData.intrinsicMatrix, cameraReferenceDimensions: cameraCalibrationData.intrinsicMatrixReferenceDimensions) delegate?.onNewData(capturedData: data) } ``` -------------------------------- ### Implement CameraManager for SwiftUI State Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Uses ObservableObject to bridge camera capture data with SwiftUI views. Requires conformance to CaptureDataReceiver to handle incoming texture data. ```swift import SwiftUI import Combine import simd import AVFoundation class CameraManager: ObservableObject, CaptureDataReceiver { var capturedData: CameraCapturedData @Published var isFilteringDepth: Bool { didSet { controller.isFilteringEnabled = isFilteringDepth } } @Published var orientation = UIDevice.current.orientation @Published var waitingForCapture = false @Published var processingCapturedResult = false @Published var dataAvailable = false let controller: CameraController var cancellables = Set() var session: AVCaptureSession { controller.captureSession } init() { capturedData = CameraCapturedData() controller = CameraController() controller.isFilteringEnabled = true controller.startStream() isFilteringDepth = controller.isFilteringEnabled // Subscribe to device orientation changes NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification).sink { _ in self.orientation = UIDevice.current.orientation }.store(in: &cancellables) controller.delegate = self } func startPhotoCapture() { controller.capturePhoto() waitingForCapture = true } func resumeStream() { controller.startStream() processingCapturedResult = false waitingForCapture = false } func onNewData(capturedData: CameraCapturedData) { DispatchQueue.main.async { if !self.processingCapturedResult { self.capturedData.depth = capturedData.depth self.capturedData.colorY = capturedData.colorY self.capturedData.colorCbCr = capturedData.colorCbCr self.capturedData.cameraIntrinsics = capturedData.cameraIntrinsics self.capturedData.cameraReferenceDimensions = capturedData.cameraReferenceDimensions if self.dataAvailable == false { self.dataAvailable = true } } } } } // Data container for captured textures and camera parameters class CameraCapturedData { var depth: MTLTexture? var colorY: MTLTexture? var colorCbCr: MTLTexture? var cameraIntrinsics: matrix_float3x3 var cameraReferenceDimensions: CGSize init(depth: MTLTexture? = nil, colorY: MTLTexture? = nil, colorCbCr: MTLTexture? = nil, cameraIntrinsics: matrix_float3x3 = matrix_float3x3(), cameraReferenceDimensions: CGSize = .zero) { self.depth = depth self.colorY = colorY self.colorCbCr = colorCbCr self.cameraIntrinsics = cameraIntrinsics self.cameraReferenceDimensions = cameraReferenceDimensions } } ``` -------------------------------- ### Fragment Shader for Colored Point Cloud Rendering Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt A Metal fragment shader that renders points for a point cloud. It discards fragments with a depth less than 1.0 and returns the color calculated by the vertex shader. ```metal // Fragment shader for colored point cloud fragment half4 pointCloudFragmentShader(ParticleVertexInOut in [[stage_in]]) { if (in.depth < 1.0f) discard_fragment(); return in.color; } ``` -------------------------------- ### Fragment Shader for Depth Visualization Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt A Metal fragment shader that visualizes depth data by sampling a depth texture and converting the depth value to a color using the Jet color scheme. It requires minDepth and maxDepth uniforms to normalize the depth values. ```metal // Fragment shader for depth visualization with Jet colors fragment half4 planeFragmentShaderDepth( ColorInOut in [[stage_in]], texture2d textureDepth [[ texture(0) ]], constant float &minDepth [[buffer(0)]], constant float &maxDepth [[buffer(1)]]) { constexpr sampler colorSampler(address::clamp_to_edge, filter::nearest); float val = (textureDepth.sample(colorSampler, in.texCoord).r) / (maxDepth - minDepth); half4 rgbaResult = getJetColorsFromNormalizedVal(half(val)); if(val < minDepth || val > maxDepth) { rgbaResult = 0; } return rgbaResult; } ``` -------------------------------- ### Save JSON Data to Documents Directory Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Saves JSON data to a file in the application's documents directory. The filename is appended with '.json'. This function uses `FileManager` to handle file operations. ```swift // Save JSON to documents directory func writeJson(json: Data, filename: String) { guard let url = try? FileManager.default.url( for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true ).appendingPathComponent(filename + ".json") else { return } try? json.write(to: url) } ``` -------------------------------- ### Convert Depth Pixel Buffer to 2D Float Array Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Converts a CVPixelBuffer containing depth data into a 2D array of Float32 values. Ensure the pixel buffer is locked before calling this function. ```swift // Convert depth pixel buffer to 2D array of Float32 values func convertDepthData(depthMap: CVPixelBuffer) -> [[Float32]] { let width = CVPixelBufferGetWidth(depthMap) let height = CVPixelBufferGetHeight(depthMap) var convertedDepthMap: [[Float32]] = Array( repeating: Array(repeating: 0, count: width), count: height ) CVPixelBufferLockBaseAddress(depthMap, CVPixelBufferLockFlags(rawValue: 2)) let floatBuffer = unsafeBitCast( CVPixelBufferGetBaseAddress(depthMap), to: UnsafeMutablePointer.self ) for row in 0 ..< height { for col in 0 ..< width { convertedDepthMap[row][col] = floatBuffer[width * row + col] } } CVPixelBufferUnlockBaseAddress(depthMap, CVPixelBufferLockFlags(rawValue: 2)) return convertedDepthMap } ``` -------------------------------- ### Convert Normalized Depth to Jet Color Scheme Source: https://context7.com/sakutama-11/lidardepthcapture/llms.txt Converts a normalized depth value (0.0 to 1.0) into a color using the Jet color map. Values outside the typical range might result in black. This function is useful for visualizing depth data. ```metal #include using namespace metal; // Convert depth value to Jet color scheme (blue -> cyan -> green -> yellow -> red) static half4 getJetColorsFromNormalizedVal(half val) { half4 res; if(val <= 0.01h) return half4(); res.r = 1.5h - fabs(4.0h * val - 3.0h); res.g = 1.5h - fabs(4.0h * val - 2.0h); res.b = 1.5h - fabs(4.0h * val - 1.0h); res.a = 1.0h; res = clamp(res, 0.0h, 1.0h); return res; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.