├── Examples └── Photons │ ├── Packages │ └── RealityKitContent │ │ ├── README.md │ │ ├── Sources │ │ └── RealityKitContent │ │ │ ├── RealityKitContent.swift │ │ │ └── RealityKitContent.rkassets │ │ │ ├── Materials │ │ │ └── GridMaterial.usda │ │ │ └── Immersive.usda │ │ ├── Package.realitycomposerpro │ │ ├── PluginData │ │ │ └── AF09ED6F-1707-48FD-8720-65B998362C09 │ │ │ │ └── ShaderGraphEditorPluginID │ │ │ │ └── ShaderGraphEditorPluginID │ │ ├── WorkspaceData │ │ │ ├── Settings.rcprojectdata │ │ │ ├── SceneMetadataList.json │ │ │ └── lakiiinbor.rcuserdata │ │ └── ProjectData │ │ │ └── main.json │ │ └── Package.swift │ ├── Photons │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.solidimagestack │ │ │ ├── Back.solidimagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Front.solidimagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Middle.solidimagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ └── hello_particles.imageset │ │ │ ├── hello_particles.jpg │ │ │ └── Contents.json │ ├── ECS │ │ ├── SettingsComponent.swift │ │ ├── ParticlesUpdateSystem.swift │ │ ├── SimulationSystem.swift │ │ └── SceneUpdateSystem.swift │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── AppModel.swift │ ├── Info.plist │ ├── PhotonsApp.swift │ ├── Scene │ │ ├── SceneMeshAssembler.metal │ │ ├── SceneCollision.metal │ │ ├── SceneMeshBuilder.swift │ │ ├── SceneMeshProvider.swift │ │ ├── SceneMeshAssembler.swift │ │ └── SceneCollisionEncoder.swift │ ├── Particles │ │ ├── ParticleMeshUpdater.metal │ │ └── ParticleMeshBuilder.swift │ ├── UI │ │ ├── ToggleImmersiveSpaceButton.swift │ │ ├── ContentView.swift │ │ ├── ViewModel.swift │ │ └── ImmersiveView.swift │ ├── Utils.h │ └── Simulation │ │ ├── Simulation.metal │ │ └── Simulation.swift │ ├── Photons.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── TheSpatialPhoto.xcscheme │ └── project.pbxproj │ └── README.md └── .gitignore /Examples/Photons/Packages/RealityKitContent/README.md: -------------------------------------------------------------------------------- 1 | # RealityKitContent 2 | 3 | A description of this package. -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/ECS/SettingsComponent.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | 3 | struct SettingsComponent: Component { 4 | let settings: Settings 5 | } 6 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Bundle for the RealityKitContent project 4 | public let realityKitContentBundle = Bundle.module 5 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/hello_particles.imageset/hello_particles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/computer-graphics-tools/examples/HEAD/Examples/Photons/Photons/Assets.xcassets/hello_particles.imageset/hello_particles.jpg -------------------------------------------------------------------------------- /Examples/Photons/Photons.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Package.realitycomposerpro/PluginData/AF09ED6F-1707-48FD-8720-65B998362C09/ShaderGraphEditorPluginID/ShaderGraphEditorPluginID: -------------------------------------------------------------------------------- 1 | { 2 | "materialPreviewEnvironmentType" : 2, 3 | "materialPreviewObjectType" : 0 4 | } -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "vision", 5 | "scale" : "2x" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "vision", 5 | "scale" : "2x" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "vision", 5 | "scale" : "2x" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Examples/Photons/Photons.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata: -------------------------------------------------------------------------------- 1 | { 2 | "cameraPresets" : { 3 | 4 | }, 5 | "secondaryToolbarData" : { 6 | "isGridVisible" : true 7 | }, 8 | "unitDefaults" : { 9 | "kg" : "g", 10 | "kg⋅m²" : "kg⋅m²", 11 | "m" : "cm", 12 | "m\/s" : "m\/s", 13 | "m\/s²" : "m\/s²", 14 | "s" : "s", 15 | "°" : "°" 16 | } 17 | } -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/AppIcon.solidimagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "layers" : [ 7 | { 8 | "filename" : "Front.solidimagestacklayer" 9 | }, 10 | { 11 | "filename" : "Middle.solidimagestacklayer" 12 | }, 13 | { 14 | "filename" : "Back.solidimagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Assets.xcassets/hello_particles.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "hello_particles.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/AppModel.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | /// Maintains app-wide state 4 | @MainActor 5 | @Observable 6 | class AppModel { 7 | let immersiveSpaceID = "ImmersiveSpace" 8 | enum ImmersiveSpaceState { 9 | case closed 10 | case inTransition 11 | case open 12 | } 13 | var immersiveSpaceState = ImmersiveSpaceState.closed 14 | } 15 | 16 | let device = MTLCreateSystemDefaultDevice()! 17 | let commandQueue = device.makeCommandQueue()! 18 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSHandsTrackingUsageDescription 6 | For fun 7 | NSWorldSensingUsageDescription 8 | "collision" 9 | UIApplicationSceneManifest 10 | 11 | UIApplicationPreferredDefaultSceneSessionRole 12 | UIWindowSceneSessionRoleApplication 13 | UIApplicationSupportsMultipleScenes 14 | 15 | UISceneConfigurations 16 | 17 | UISceneSessionRoleImmersiveSpaceApplication 18 | 19 | 20 | UISceneInitialImmersionStyle 21 | UIImmersionStyleMixed 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/PhotonsApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct PhotonsApp: App { 5 | @State private var appModel = AppModel() 6 | @State private var viewModel = ViewModel() 7 | 8 | var body: some Scene { 9 | WindowGroup { 10 | ContentView() 11 | .environment(appModel) 12 | .environmentObject(viewModel) 13 | } 14 | .defaultSize(width: 600, height: 400) 15 | 16 | ImmersiveSpace(id: appModel.immersiveSpaceID) { 17 | ImmersiveView() 18 | .environment(appModel) 19 | .onAppear { 20 | appModel.immersiveSpaceState = .open 21 | } 22 | .onDisappear { 23 | appModel.immersiveSpaceState = .closed 24 | } 25 | } 26 | .immersionStyle(selection: .constant(.mixed), in: .mixed) 27 | .environmentObject(viewModel) 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "pathsToIds" : { 3 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "CB766F92-EE55-4A63-9401-E7B8C009764D", 4 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "65F6F990-A780-4474-B78B-572E0E4E273D", 5 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "0A9B4653-B11E-4D6A-850E-C6FCB621626C", 6 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Untitled Scene.usda" : "D560BB77-AAF3-4BDE-B7C4-989332A4688B", 7 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "66168B71-AB05-424E-8B6C-D33D6E61B08F", 8 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "AF09ED6F-1707-48FD-8720-65B998362C09", 9 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "D66134B1-3681-4A8E-AFE5-29F257229F3B" 10 | } 11 | } -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "RealityKitContent", 8 | platforms: [ 9 | .visionOS(.v1), 10 | .macOS(.v14), 11 | .iOS(.v17) 12 | ], 13 | products: [ 14 | // Products define the executables and libraries a package produces, and make them visible to other packages. 15 | .library( 16 | name: "RealityKitContent", 17 | targets: ["RealityKitContent"]), 18 | ], 19 | dependencies: [ 20 | // Dependencies declare other packages that this package depends on. 21 | // .package(url: /* package url */, from: "1.0.0"), 22 | ], 23 | targets: [ 24 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 25 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 26 | .target( 27 | name: "RealityKitContent", 28 | dependencies: []), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /Examples/Photons/README.md: -------------------------------------------------------------------------------- 1 | # Photons Project 2 | 3 | ## Overview 4 | 5 | Photons is an example project showcasing the use of particle simulation, scene reconstruction, collision handling, and tracking techniques for hands and head. This project demonstrates how to integrate RealityKit, Metal, and ARKit to create an interactive expirience with particle physics, powered by simulation-tools and metal-tools packages. 6 | 7 | ## Features 8 | 9 | - **Particle Simulation:** Implements particle simulation using Metal compute shaders. 10 | - **Scene Reconstruction:** Utilizes ARKit for real-time scene reconstruction, allowing the app to understand and interact with the physical environment. 11 | - **Collision Handling:** Uses spatial hashing and collision detection to manage interactions between particles and the environment. 12 | - **Hand and Head Tracking:** Employs ARKit to track the user's hands and head. 13 | 14 | 15 | ## Getting Started 16 | 17 | ### Prerequisites 18 | 19 | - Xcode 16 or later 20 | - visionOS 2.0 or later 21 | 22 | ### Usage 23 | 24 | 1. Launch the app on your device. 25 | 2. Use the "Reset" button to reset the particle simulation. 26 | 3. Tap the import icon to select an image from your photo library. 27 | 4. Toggle between immersive and standard modes using the immersive space button. 28 | 5. Interact with the particles using your hands and observe real-time scene reconstruction and collision handling. 29 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/ECS/ParticlesUpdateSystem.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | import Metal 3 | 4 | struct ParticlesUpdateComponent: Component { 5 | var particleMeshBuilder: ParticleMeshBuilder 6 | } 7 | 8 | struct ParticlesUpdateSystem: System { 9 | static let query = EntityQuery(where: .has(ParticlesUpdateComponent.self) && .has(SimulationComponent.self)) 10 | 11 | init(scene: RealityKit.Scene) { 12 | ParticlesUpdateComponent.registerComponent() 13 | } 14 | 15 | func update(context: SceneUpdateContext) { 16 | for entity in context.entities(matching: Self.query, updatingSystemWhen: .rendering) { 17 | guard let meshComponent = entity.components[ParticlesUpdateComponent.self], 18 | let simulationComponent = entity.components[SimulationComponent.self], 19 | let commandBuffer = commandQueue.makeCommandBuffer() 20 | else { continue } 21 | 22 | 23 | if let simulator = simulationComponent.simulator { 24 | meshComponent.particleMeshBuilder.replaceMeshBuffer( 25 | poisitionsBuffer: simulator.positions.buffer, 26 | vertexNeighbors: simulator.selfCollisionCandidates.buffer, 27 | commandBuffer: commandBuffer 28 | ) 29 | 30 | commandBuffer.commit() 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Scene/SceneMeshAssembler.metal: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace metal; 3 | 4 | kernel void assembleScenePositions(device packed_float3* outputVertices [[buffer(0)]], 5 | device const packed_float3* inputVertices [[buffer(1)]], 6 | constant uint& positionsCount [[buffer(2)]], 7 | constant uint& offset [[buffer(3)]], 8 | constant float4x4& transform [[buffer(4)]], 9 | uint gid [[thread_position_in_grid]]) 10 | { 11 | if (gid >= positionsCount) { return; } 12 | float3 position = inputVertices[gid]; 13 | float4 transformedVertex = transform * float4(position, 1.0); 14 | outputVertices[gid + offset] = transformedVertex.xyz; 15 | } 16 | 17 | kernel void assembleSceneIndices(device uint* outputIndices [[buffer(0)]], 18 | device const uint* inputIndices [[buffer(1)]], 19 | constant uint& indicesCount [[buffer(2)]], 20 | constant uint& offset [[buffer(3)]], 21 | constant uint& vertexOffset [[buffer(4)]], 22 | constant float4x4& transform [[buffer(5)]], 23 | uint gid [[thread_position_in_grid]]) 24 | { 25 | if (gid >= indicesCount) { return; } 26 | uint inputIndex = inputIndices[gid]; 27 | outputIndices[gid + offset] = inputIndex + vertexOffset; 28 | } 29 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/ECS/SimulationSystem.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | 3 | struct SimulationComponent: Component { 4 | weak var simulator: Simulation? 5 | } 6 | 7 | struct SimulationSystem: System { 8 | static let query = EntityQuery(where: .has(SimulationComponent.self)) 9 | 10 | init(scene: RealityKit.Scene) { 11 | SimulationComponent.registerComponent() 12 | } 13 | 14 | func update(context: SceneUpdateContext) { 15 | for entity in context.entities(matching: Self.query, updatingSystemWhen: .rendering) { 16 | guard 17 | let simulationComponent = entity.components[SimulationComponent.self], 18 | let leftPalm = context.scene.findEntity(named: "LeftPalm") as? AnchorEntity, 19 | let rightPalm = context.scene.findEntity(named: "RightPalm") as? AnchorEntity, 20 | let head = context.scene.findEntity(named: "Head") as? AnchorEntity, 21 | let commandBuffer = commandQueue.makeCommandBuffer() 22 | else { continue } 23 | 24 | simulationComponent.simulator?.update( 25 | leftHandPosition: leftPalm.position(relativeTo: nil), 26 | rightHandPosition: rightPalm.position(relativeTo: nil), 27 | headPosition: head.position(relativeTo: nil), 28 | commandBuffer: commandBuffer, 29 | dt: context.deltaTime 30 | ) 31 | 32 | commandBuffer.commit() 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Particles/ParticleMeshUpdater.metal: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace metal; 3 | 4 | struct ParticleVertex { 5 | half4 position; 6 | float2 uv; 7 | float2 uv2; 8 | }; 9 | 10 | kernel void updateVertexBuffer( 11 | device ParticleVertex *vertices [[buffer(0)]], 12 | const device float4 *positions [[buffer(1)]], 13 | constant uint &positionsCount [[ buffer(2) ]], 14 | const device float2 *uvs [[buffer(3)]], 15 | constant float &ratio [[ buffer(4) ]], 16 | constant float &particleSize [[ buffer(5) ]], 17 | uint id [[ thread_position_in_grid ]] 18 | ) { 19 | 20 | if (id >= positionsCount) { return; } 21 | uint particleIndex = id / 4; 22 | uint vertexInParticleIndex = id % 4; 23 | 24 | float4 particlePosition = positions[particleIndex]; 25 | float halfSize = particleSize * -0.5; 26 | 27 | float3 newPosition = particlePosition.xyz; 28 | 29 | float2 particleUV = uvs[particleIndex]; 30 | float2 uv[4] = { 31 | particleUV + float2(-halfSize * ratio, -halfSize), 32 | particleUV + float2(halfSize * ratio, -halfSize), 33 | particleUV + float2(-halfSize * ratio, halfSize), 34 | particleUV + float2(halfSize * ratio, halfSize) 35 | }; 36 | 37 | const float2 uv2[4] = { 38 | float2(0.0, 0.0), float2(1.0, 0.0), float2(0.0, 1.0), float2(1.0, 1.0) 39 | }; 40 | 41 | vertices[id].position = half4(half3(newPosition), 1.0); 42 | 43 | vertices[id].uv = uv[vertexInParticleIndex % 4]; 44 | vertices[id].uv2 = uv2[vertexInParticleIndex % 4]; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/UI/ToggleImmersiveSpaceButton.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct ToggleImmersiveSpaceButton: View { 5 | @Environment(AppModel.self) private var appModel 6 | 7 | @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace 8 | @Environment(\.openImmersiveSpace) private var openImmersiveSpace 9 | 10 | var body: some View { 11 | Button { 12 | Task { @MainActor in 13 | switch appModel.immersiveSpaceState { 14 | case .open: 15 | appModel.immersiveSpaceState = .inTransition 16 | await dismissImmersiveSpace() 17 | 18 | case .closed: 19 | appModel.immersiveSpaceState = .inTransition 20 | switch await openImmersiveSpace(id: appModel.immersiveSpaceID) { 21 | case .opened: 22 | break 23 | 24 | case .userCancelled, .error: 25 | fallthrough 26 | @unknown default: 27 | appModel.immersiveSpaceState = .closed 28 | } 29 | 30 | case .inTransition: 31 | break 32 | } 33 | } 34 | } label: { 35 | Image(systemName: appModel.immersiveSpaceState == .open ? "rectangle.portrait.fill" : "rectangle.portrait") 36 | } 37 | .disabled(appModel.immersiveSpaceState == .inTransition) 38 | .animation(.none, value: 0) 39 | .fontWeight(.semibold) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/ECS/SceneUpdateSystem.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | import SwiftUI 3 | 4 | struct SceneMeshProviderComponent: Component { 5 | var meshBuilder: SceneMeshBuilder 6 | } 7 | 8 | #if !targetEnvironment(simulator) 9 | struct SceneUpdateSystem: System { 10 | static let query = EntityQuery( 11 | where: .has(SceneMeshProviderComponent.self) && .has(SettingsComponent.self) 12 | ) 13 | 14 | init(scene: RealityKit.Scene) { 15 | SceneMeshProviderComponent.registerComponent() 16 | } 17 | 18 | func update(context: SceneUpdateContext) { 19 | for entity in context.entities(matching: Self.query, updatingSystemWhen: .rendering) { 20 | guard let providerComponent = entity.components[SceneMeshProviderComponent.self], 21 | let settingsComponent = entity.components[SettingsComponent.self] 22 | else { 23 | continue 24 | } 25 | 26 | if let entity = entity.findEntity(named: "SceneDebugEntity") { 27 | entity.components.set(OpacityComponent(opacity: settingsComponent.settings.showWireframe ? 1.0 : 0.0)) 28 | } 29 | 30 | if providerComponent.meshBuilder.needsMeshUpdate, let resource = try? providerComponent.meshBuilder.createMeshResource() { 31 | updateSceneMeshEntities(entity: entity, meshResource: resource) 32 | } 33 | 34 | entity.components[SceneMeshProviderComponent.self] = providerComponent 35 | } 36 | } 37 | 38 | private func updateSceneMeshEntities(entity: Entity, meshResource: MeshResource) { 39 | let occlusionMaterial = OcclusionMaterial() 40 | var lineMaterial = SimpleMaterial() 41 | lineMaterial.triangleFillMode = .lines 42 | 43 | for i in 0..<2 { 44 | let entityName = i == 0 ? "SceneEntity" : "SceneDebugEntity" 45 | let material: RealityKit.Material = i == 0 ? occlusionMaterial : lineMaterial 46 | 47 | if let existingEntity = entity.findEntity(named: entityName) { 48 | existingEntity.components.set(ModelComponent(mesh: meshResource, materials: [material])) 49 | } else { 50 | let newEntity = ModelEntity(mesh: meshResource, materials: [material]) 51 | newEntity.name = entityName 52 | entity.addChild(newEntity) 53 | } 54 | } 55 | } 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef Utils_h 2 | #define Utils_h 3 | 4 | #include 5 | using namespace metal; 6 | 7 | METAL_FUNC float3 closestPointTriangle(float3 p0, float3 p1, float3 p2, float3 p, thread float3& uvw) { 8 | float b0 = 1.0 / 3.0; 9 | float b1 = b0; 10 | float b2 = b0; 11 | 12 | float3 d1 = p1 - p0; 13 | float3 d2 = p2 - p0; 14 | float3 pp0 = p - p0; 15 | float a = length_squared(d1); 16 | float b = dot(d2, d1); 17 | float c = dot(pp0, d1); 18 | float d = b; 19 | float e = length_squared(d2); 20 | float f = dot(pp0, d2); 21 | float det = a * e - b * d; 22 | 23 | if (det != 0.0) { 24 | float s = (c * e - b * f) / det; 25 | float t = (a * f - c * d) / det; 26 | b0 = 1.0 - s - t; // inside triangle 27 | b1 = s; 28 | b2 = t; 29 | if (b0 < 0.0) { // on edge 1-2 30 | float3 d = p2 - p1; 31 | float d2 = length_squared(d); 32 | float t = (d2 == 0.0) ? 0.5 : dot(d, p - p1) / d2; 33 | t = saturate(t); 34 | 35 | b0 = 0.0; 36 | b1 = (1.0 - t); 37 | b2 = t; 38 | } 39 | else if (b1 < 0.0) { // on edge 2-0 40 | float3 d = p0 - p2; 41 | float d2 = length_squared(d); 42 | float t = (d2 == 0.0) ? 0.5 : dot(d, p - p2) / d2; 43 | t = saturate(t); 44 | 45 | b1 = 0.0; 46 | b2 = (1.0 - t); 47 | b0 = t; 48 | } 49 | else if (b2 < 0.0) { // on edge 0-1 50 | float3 d = p1 - p0; 51 | float d2 = length_squared(d); 52 | float t = (d2 == 0.0) ? 0.5 : dot(d, (p - p0)) / d2; 53 | t = saturate(t); 54 | 55 | b2 = 0.0; 56 | b0 = (1.0 - t); 57 | b1 = t; 58 | } 59 | } 60 | 61 | uvw = float3(b0, b1, b2); 62 | 63 | return b0 * p0 + b1 * p1 + b2 * p2; 64 | } 65 | 66 | METAL_FUNC float3 computeFriction(float3 norm, float error, float3 relVelocity, float frictionCoeff) { 67 | float3 friction = float3(0); 68 | if (frictionCoeff > 0 && error > 0) { 69 | float3 tanVelocity = relVelocity - norm * dot(relVelocity, norm); 70 | float tanLength = length(tanVelocity); 71 | float maxTanLength = error * frictionCoeff; 72 | friction = -tanVelocity * min(maxTanLength / tanLength, 1.0); 73 | } 74 | 75 | return friction; 76 | } 77 | 78 | #endif /* Utils_h */ 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | .swiftpm 92 | *.DS_Store 93 | Package.resolved 94 | *.swiftformat 95 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Scene/SceneCollision.metal: -------------------------------------------------------------------------------- 1 | #include "../Utils.h" 2 | 3 | kernel void handleVertexTriangleCollision( 4 | device float4 *positions [[ buffer(0) ]], 5 | constant float4 *prevPositions [[ buffer(1) ]], 6 | constant uint *collisionCandidates [[ buffer(2) ]], 7 | constant packed_float3 *wordPositions [[ buffer(3) ]], 8 | constant packed_uint3* sceneTriangles [[buffer(4) ]], 9 | constant float &frictionCoeff [[ buffer(5) ]], 10 | constant uint& maxCollisionCandidatesCount [[ buffer(6) ]], 11 | constant uint &gridSize [[ buffer(7) ]], 12 | uint gid [[ thread_position_in_grid ]] 13 | ) { 14 | if (gid >= gridSize) { return; } 15 | uint vertexIndex = gid; 16 | 17 | float3 position = positions[vertexIndex].xyz; 18 | float3 prevPosition = prevPositions[vertexIndex].xyz; 19 | float3 velocity = position - prevPosition; 20 | 21 | const float proximity = 0.025; 22 | float3 totalCorrection = 0.0; 23 | 24 | for (uint i = 0; i < maxCollisionCandidatesCount; i++) { 25 | uint triangleIndex = collisionCandidates[gid * maxCollisionCandidatesCount + i]; 26 | if (triangleIndex == UINT_MAX) { continue; } 27 | uint3 triangleVertices = sceneTriangles[triangleIndex].xyz; 28 | 29 | 30 | float3 uvw; 31 | float3 closestPoint = closestPointTriangle( 32 | wordPositions[triangleVertices.x].xyz, 33 | wordPositions[triangleVertices.y].xyz, 34 | wordPositions[triangleVertices.z].xyz, 35 | position, 36 | uvw 37 | ); 38 | 39 | float3 pointToVertex = position - closestPoint; 40 | float distance = length(pointToVertex); 41 | if (!isnormal(distance)) { continue; } 42 | float3 direction = pointToVertex / distance; 43 | float error = distance - proximity; 44 | 45 | if (error < 0) { 46 | float3 triangleNormal = cross(wordPositions[triangleVertices.y].xyz - wordPositions[triangleVertices.x].xyz, wordPositions[triangleVertices.z].xyz - wordPositions[triangleVertices.x].xyz); 47 | float3 triangleVelocity = 0; 48 | float3 relativeVelocity = velocity - triangleVelocity; 49 | float normalProjection = dot(triangleNormal, direction); 50 | float3 correction = error * direction; 51 | if (normalProjection < 0.0) { continue; } 52 | 53 | correction *= sign(normalProjection); 54 | 55 | const float relaxation = 1.0; 56 | float3 friction = computeFriction(direction, -error, relativeVelocity, frictionCoeff); 57 | totalCorrection += -correction * relaxation + friction; 58 | break; 59 | } 60 | } 61 | 62 | float3 newPosition = position + totalCorrection; 63 | positions[vertexIndex].xyz = newPosition; 64 | } 65 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Scene/SceneMeshBuilder.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | 3 | struct SimpleVertex { 4 | var position: (Float, Float, Float) = (0, 0, 0) 5 | } 6 | 7 | extension SimpleVertex { 8 | static var vertexAttributes: [LowLevelMesh.Attribute] = [ 9 | .init(semantic: .position, format: .float3, offset: 0), 10 | ] 11 | 12 | static var vertexLayouts: [LowLevelMesh.Layout] = [ 13 | .init(bufferIndex: 0, bufferStride: MemoryLayout.stride) 14 | ] 15 | 16 | static var descriptor: LowLevelMesh.Descriptor { 17 | var descriptor = LowLevelMesh.Descriptor() 18 | descriptor.vertexAttributes = SimpleVertex.vertexAttributes 19 | descriptor.vertexLayouts = SimpleVertex.vertexLayouts 20 | descriptor.indexType = .uint32 21 | 22 | return descriptor 23 | } 24 | } 25 | 26 | final class SceneMeshBuilder { 27 | enum Error: Swift.Error { 28 | case missingGeometry 29 | case bufferCreationFailed 30 | } 31 | 32 | private(set) var needsMeshUpdate = false 33 | private(set) var geometry: SimpleGeometry? 34 | private var lowLevelMesh: LowLevelMesh? 35 | 36 | func updateGeometry(geometry: SimpleGeometry) { 37 | self.geometry = geometry 38 | needsMeshUpdate = true 39 | } 40 | 41 | @MainActor func createMeshResource() throws -> MeshResource { 42 | let lowLevelMesh = try buildMesh() 43 | let resource = try MeshResource(from: lowLevelMesh) 44 | self.lowLevelMesh = lowLevelMesh 45 | needsMeshUpdate = false 46 | 47 | return resource 48 | } 49 | 50 | @MainActor func buildMesh() throws -> LowLevelMesh { 51 | guard let geometry else { throw Error.missingGeometry } 52 | var desc = SimpleVertex.descriptor 53 | desc.vertexCapacity = geometry.positionsCount 54 | desc.indexCapacity = geometry.trianglesCount * 3 55 | 56 | let lowMesh = try LowLevelMesh(descriptor: desc) 57 | guard let commandBuffer = commandQueue.makeCommandBuffer() else { throw Error.bufferCreationFailed } 58 | 59 | let positionsBuffer = lowMesh.replace(bufferIndex: 0, using: commandBuffer) 60 | let indicesBuffer = lowMesh.replaceIndices(using: commandBuffer) 61 | commandBuffer.blit { encoder in 62 | encoder.copy(from: geometry.positions, sourceOffset: 0, to: positionsBuffer, destinationOffset: 0, size: geometry.positions.length) 63 | encoder.copy(from: geometry.triangles, sourceOffset: 0, to: indicesBuffer, destinationOffset: 0, size: geometry.triangles.length) 64 | } 65 | commandBuffer.commit() 66 | let meshBounds = BoundingBox(min: [-100, -100, -100], max: [100, 100, 100]) 67 | lowMesh.parts.replaceAll([ 68 | LowLevelMesh.Part( 69 | indexCount: geometry.trianglesCount * 3, 70 | topology: .triangle, 71 | bounds: meshBounds 72 | ) 73 | ]) 74 | 75 | return lowMesh 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /Examples/Photons/Photons.xcodeproj/xcshareddata/xcschemes/TheSpatialPhoto.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 44 | 46 | 52 | 53 | 54 | 55 | 61 | 63 | 69 | 70 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/UI/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import _PhotosUI_SwiftUI 3 | import RealityKit 4 | import RealityKitContent 5 | 6 | struct ContentView: View { 7 | @EnvironmentObject private var viewModel: ViewModel 8 | @State private var selectedItem: PhotosPickerItem? 9 | 10 | var body: some View { 11 | VStack { 12 | ZStack { 13 | VStack { 14 | HStack { 15 | Spacer() 16 | Button { 17 | viewModel.reset() 18 | } label: { 19 | Text("Reset") 20 | } 21 | .padding() 22 | } 23 | Spacer() 24 | } 25 | VStack { 26 | Spacer() 27 | HStack { 28 | PhotosPicker(selection: $selectedItem, matching: .images, photoLibrary: .shared()) { 29 | Image(systemName: "plus.rectangle") 30 | } 31 | .onChange(of: selectedItem) { _, _ in 32 | Task { 33 | if let data = try? await selectedItem?.loadTransferable(type: Data.self), 34 | let image = UIImage(data: data) { 35 | viewModel.setSelectedImage(image) 36 | } 37 | } 38 | } 39 | .frame(width: 72) 40 | ToggleImmersiveSpaceButton() 41 | .frame(width: 72) 42 | Toggle(isOn: $viewModel.settings.showWireframe) { 43 | Image(systemName: "squareshape.split.3x3") 44 | } 45 | .toggleStyle(.button) 46 | .frame(width: 72) 47 | } 48 | Spacer() 49 | }.padding(.horizontal, 128) 50 | } 51 | } 52 | .padding() 53 | } 54 | } 55 | 56 | #Preview(windowStyle: .automatic) { 57 | ContentView() 58 | .environment(AppModel()) 59 | .environmentObject(ViewModel()) 60 | } 61 | 62 | struct ProfileImage: Transferable { 63 | let image: Image 64 | 65 | static var transferRepresentation: some TransferRepresentation { 66 | DataRepresentation(importedContentType: .image) { data in 67 | #if canImport(AppKit) 68 | guard let nsImage = NSImage(data: data) else { 69 | throw TransferError.importFailed 70 | } 71 | let image = Image(nsImage: nsImage) 72 | return ProfileImage(image: image) 73 | #elseif canImport(UIKit) 74 | guard let uiImage = UIImage(data: data) else { 75 | throw NSError(domain: "failed to load", code: 293583) 76 | } 77 | let image = Image(uiImage: uiImage) 78 | return ProfileImage(image: image) 79 | #else 80 | throw TransferError.importFailed 81 | #endif 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json: -------------------------------------------------------------------------------- 1 | { 2 | "0A9B4653-B11E-4D6A-850E-C6FCB621626C" : { 3 | "objectMetadataList" : [ 4 | [ 5 | "0A9B4653-B11E-4D6A-850E-C6FCB621626C", 6 | "Root" 7 | ], 8 | { 9 | "isExpanded" : true, 10 | "isLocked" : false 11 | } 12 | ] 13 | }, 14 | "65F6F990-A780-4474-B78B-572E0E4E273D" : { 15 | "objectMetadataList" : [ 16 | [ 17 | "65F6F990-A780-4474-B78B-572E0E4E273D", 18 | "Root" 19 | ], 20 | { 21 | "isExpanded" : true, 22 | "isLocked" : false 23 | } 24 | ] 25 | }, 26 | "66168B71-AB05-424E-8B6C-D33D6E61B08F" : { 27 | "objectMetadataList" : [ 28 | [ 29 | "66168B71-AB05-424E-8B6C-D33D6E61B08F", 30 | "Root" 31 | ], 32 | { 33 | "isExpanded" : true, 34 | "isLocked" : false 35 | } 36 | ] 37 | }, 38 | "AF09ED6F-1707-48FD-8720-65B998362C09" : { 39 | "objectMetadataList" : [ 40 | [ 41 | "AF09ED6F-1707-48FD-8720-65B998362C09", 42 | "Root", 43 | "BillboardMaterial", 44 | "ParticleSpacePosition" 45 | ], 46 | { 47 | "isExpanded" : true, 48 | "isLocked" : false 49 | }, 50 | [ 51 | "AF09ED6F-1707-48FD-8720-65B998362C09", 52 | "Root", 53 | "Sphere", 54 | "DefaultMaterial" 55 | ], 56 | { 57 | "isExpanded" : true, 58 | "isLocked" : false 59 | }, 60 | [ 61 | "AF09ED6F-1707-48FD-8720-65B998362C09", 62 | "Root", 63 | "BillboardMaterial" 64 | ], 65 | { 66 | "isExpanded" : true, 67 | "isLocked" : false 68 | }, 69 | [ 70 | "AF09ED6F-1707-48FD-8720-65B998362C09", 71 | "Root", 72 | "BillboardMaterial", 73 | "ViewFrameTest" 74 | ], 75 | { 76 | "isExpanded" : true, 77 | "isLocked" : false 78 | }, 79 | [ 80 | "AF09ED6F-1707-48FD-8720-65B998362C09", 81 | "Root" 82 | ], 83 | { 84 | "isExpanded" : true, 85 | "isLocked" : false 86 | }, 87 | [ 88 | "AF09ED6F-1707-48FD-8720-65B998362C09", 89 | "Root", 90 | "BillboardMaterial", 91 | "SimpleMath" 92 | ], 93 | { 94 | "isExpanded" : true, 95 | "isLocked" : false 96 | }, 97 | [ 98 | "AF09ED6F-1707-48FD-8720-65B998362C09", 99 | "Root", 100 | "BillboardMaterial", 101 | "ViewFrame" 102 | ], 103 | { 104 | "isExpanded" : true, 105 | "isLocked" : false 106 | } 107 | ] 108 | }, 109 | "CB766F92-EE55-4A63-9401-E7B8C009764D" : { 110 | "objectMetadataList" : [ 111 | [ 112 | "CB766F92-EE55-4A63-9401-E7B8C009764D", 113 | "Root", 114 | "GridMaterial" 115 | ], 116 | { 117 | "isExpanded" : true, 118 | "isLocked" : false 119 | }, 120 | [ 121 | "CB766F92-EE55-4A63-9401-E7B8C009764D", 122 | "Root" 123 | ], 124 | { 125 | "isExpanded" : true, 126 | "isLocked" : false 127 | } 128 | ] 129 | }, 130 | "D560BB77-AAF3-4BDE-B7C4-989332A4688B" : { 131 | "objectMetadataList" : [ 132 | 133 | ] 134 | }, 135 | "D66134B1-3681-4A8E-AFE5-29F257229F3B" : { 136 | "objectMetadataList" : [ 137 | [ 138 | "D66134B1-3681-4A8E-AFE5-29F257229F3B", 139 | "Root", 140 | "GridMaterial", 141 | "GridMaterial" 142 | ], 143 | { 144 | "isExpanded" : true, 145 | "isLocked" : false 146 | }, 147 | [ 148 | "D66134B1-3681-4A8E-AFE5-29F257229F3B", 149 | "Root" 150 | ], 151 | { 152 | "isExpanded" : true, 153 | "isLocked" : false 154 | } 155 | ] 156 | } 157 | } -------------------------------------------------------------------------------- /Examples/Photons/Photons/Scene/SceneMeshProvider.swift: -------------------------------------------------------------------------------- 1 | import Metal 2 | import ARKit 3 | import RealityKit 4 | 5 | public struct SimpleGeometry { 6 | let positions: MTLBuffer 7 | let triangles: MTLBuffer 8 | let positionsCount: Int 9 | let trianglesCount: Int 10 | 11 | init(positions: MTLBuffer, triangles: MTLBuffer, positionsCount: Int, trianglesCount: Int) { 12 | self.positions = positions 13 | self.triangles = triangles 14 | self.positionsCount = positionsCount 15 | self.trianglesCount = trianglesCount 16 | } 17 | } 18 | 19 | struct AnchorGeometry { 20 | let vertices: GeometrySource 21 | let faces: GeometryElement 22 | var transform: simd_float4x4 23 | let vertexCount: Int 24 | let faceCount: Int 25 | } 26 | 27 | final class SceneMeshProvider { 28 | private let session = ARKitSession() 29 | private let sceneReconstruction = SceneReconstructionProvider() 30 | private var anchorGeometries: [UUID: AnchorGeometry] = [:] 31 | private var cancellable: Task? 32 | private let assembler: MeshAssembler 33 | 34 | var onMeshUpdate: ((SimpleGeometry, AnchorUpdate.Event) -> Void)? 35 | var started: Bool = false 36 | 37 | init(device: MTLDevice) throws { 38 | assembler = try MeshAssembler(device: device) 39 | #if !targetEnvironment(simulator) 40 | setupARSession() 41 | #endif 42 | } 43 | 44 | func forceUpdate() { 45 | #if !targetEnvironment(simulator) 46 | guard started else { return } 47 | guard !sceneReconstruction.allAnchors.isEmpty else { return } 48 | sceneReconstruction.allAnchors.forEach { achor in 49 | updateAnchorGeometry(achor) 50 | } 51 | 52 | assembler.assembleMeshes(from: anchorGeometries) { [weak self] collider in 53 | self?.onMeshUpdate?(collider, .added) 54 | } 55 | #endif 56 | } 57 | 58 | private func setupARSession() { 59 | Task { 60 | do { 61 | try await session.run([sceneReconstruction]) 62 | subscribeToContinuousUpdates() 63 | started = true 64 | } catch { 65 | print("Failed to run session: \(error)") 66 | } 67 | } 68 | } 69 | 70 | private func subscribeToContinuousUpdates() { 71 | cancellable = Task { 72 | for await update in sceneReconstruction.anchorUpdates { 73 | await handleAnchorUpdate(update) 74 | } 75 | } 76 | } 77 | 78 | private func handleAnchorUpdate(_ update: AnchorUpdateSequence.Iterator.Element) async { 79 | switch update.event { 80 | case .added: 81 | updateAnchorGeometry(update.anchor) 82 | case .updated: 83 | anchorGeometries[update.anchor.id]?.transform = update.anchor.originFromAnchorTransform 84 | case .removed: 85 | removeAnchorGeometry(update.anchor.id) 86 | 87 | } 88 | 89 | assembler.assembleMeshes(from: anchorGeometries) { [weak self] collider in 90 | guard let self else { return } 91 | self.onMeshUpdate?(collider, update.event) 92 | } 93 | } 94 | 95 | private func updateAnchorGeometry(_ anchor: MeshAnchor) { 96 | let geometry = anchor.geometry 97 | let transform = anchor.originFromAnchorTransform 98 | 99 | let anchorGeometry = AnchorGeometry( 100 | vertices: geometry.vertices, 101 | faces: geometry.faces, 102 | transform: transform, 103 | vertexCount: geometry.vertices.count, 104 | faceCount: geometry.faces.count 105 | ) 106 | 107 | anchorGeometries[anchor.id] = anchorGeometry 108 | } 109 | 110 | private func removeAnchorGeometry(_ anchorID: UUID) { 111 | anchorGeometries.removeValue(forKey: anchorID) 112 | } 113 | 114 | deinit { 115 | cancellable?.cancel() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Scene/SceneMeshAssembler.swift: -------------------------------------------------------------------------------- 1 | import Metal 2 | import ARKit 3 | 4 | final class MeshAssembler { 5 | private let device: MTLDevice 6 | private let positionsPipelineState: MTLComputePipelineState 7 | private let indicesPipelineState: MTLComputePipelineState 8 | 9 | private var outputVertexBuffer: MTLBuffer? 10 | private var outputIndexBuffer: MTLBuffer? 11 | 12 | private(set) var totalVertexCount: Int = 0 13 | private(set) var totalIndexCount: Int = 0 14 | 15 | init(device: MTLDevice) throws { 16 | self.device = device 17 | 18 | let library = try device.makeDefaultLibrary(bundle: .main) 19 | positionsPipelineState = try library.computePipelineState(function: "assembleScenePositions") 20 | indicesPipelineState = try library.computePipelineState(function: "assembleSceneIndices") 21 | } 22 | 23 | func assembleMeshes(from geometries: [UUID: AnchorGeometry], completion: @escaping (SimpleGeometry) -> Void) { 24 | computeOutputBufferSizes(from: geometries) 25 | createOutputBufffers() 26 | 27 | guard let commandBuffer = commandQueue.makeCommandBuffer() else { return } 28 | 29 | var vertexOffset: Int = 0 30 | var indexOffset: Int = 0 31 | 32 | guard let computeEncoder = commandBuffer.makeComputeCommandEncoder(dispatchType: .concurrent) else { return } 33 | for geometry in geometries.values { 34 | encodePositionsKernel(computeEncoder: computeEncoder, geometry: geometry, vertexOffset: vertexOffset) 35 | encodeIndicesKernel(computeEncoder: computeEncoder, geometry: geometry, indexOffset: indexOffset, vertexOffset: UInt32(vertexOffset)) 36 | 37 | vertexOffset += geometry.vertexCount 38 | indexOffset += geometry.faceCount * 3 39 | } 40 | 41 | computeEncoder.endEncoding() 42 | 43 | commandBuffer.addCompletedHandler { [weak self] _ in 44 | guard let self, let outputVertexBuffer, let outputIndexBuffer else { return } 45 | completion( 46 | .init( 47 | positions: outputVertexBuffer, 48 | triangles: outputIndexBuffer, 49 | positionsCount: self.totalVertexCount, 50 | trianglesCount: self.totalIndexCount / 3 51 | ) 52 | ) 53 | } 54 | commandBuffer.commit() 55 | } 56 | 57 | private func encodePositionsKernel(computeEncoder: MTLComputeCommandEncoder, geometry: AnchorGeometry, vertexOffset: Int) { 58 | computeEncoder.setBuffer(outputVertexBuffer, offset: 0, index: 0) 59 | computeEncoder.setBuffer(geometry.vertices.buffer, offset: 0, index: 1) 60 | 61 | computeEncoder.setValue(UInt32(geometry.vertexCount), at: 2) 62 | computeEncoder.setValue(UInt32(vertexOffset), at: 3) 63 | computeEncoder.setValue(geometry.transform, at: 4) 64 | 65 | computeEncoder.dispatch1d(state: positionsPipelineState, exactlyOrCovering: geometry.vertexCount) 66 | } 67 | 68 | private func encodeIndicesKernel(computeEncoder: MTLComputeCommandEncoder, geometry: AnchorGeometry, indexOffset: Int, vertexOffset: UInt32) { 69 | computeEncoder.setBuffer(outputIndexBuffer, offset: 0, index: 0) 70 | computeEncoder.setBuffer(geometry.faces.buffer, offset: 0, index: 1) 71 | 72 | computeEncoder.setValue(UInt32(geometry.faceCount * 3), at: 2) 73 | computeEncoder.setValue(UInt32(indexOffset), at: 3) 74 | computeEncoder.setValue(vertexOffset, at: 4) 75 | computeEncoder.setValue(geometry.transform, at: 5) 76 | 77 | computeEncoder.dispatch1d(state: indicesPipelineState, exactlyOrCovering: geometry.faceCount * 3) 78 | } 79 | 80 | private func computeOutputBufferSizes(from geometries: [UUID: AnchorGeometry]) { 81 | totalVertexCount = geometries.values.reduce(0) { $0 + $1.vertexCount } 82 | totalIndexCount = geometries.values.reduce(0) { $0 + $1.faceCount * 3 } 83 | } 84 | 85 | private func createOutputBufffers() { 86 | outputVertexBuffer = device.makeBuffer(length: MemoryLayout.size * 3 * totalVertexCount, options: []) 87 | outputIndexBuffer = device.makeBuffer(length: MemoryLayout.size * totalIndexCount, options: []) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/UI/ViewModel.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | final class ViewModel: ObservableObject { 4 | @Published var selectedImage: UIImage { 5 | didSet { 6 | let correctedImage = selectedImage.correctlyOrientedImage() 7 | let (particles, uvs) = Self.buildParticles(for: correctedImage) 8 | do { 9 | particleMeshBuilder = try .init( 10 | device: device, 11 | positions: particles, 12 | uvs: uvs, 13 | cellSize: SimulationConstants.cellSize, 14 | size: correctedImage.size 15 | ) 16 | simulator = try Simulation(device: device, positions: particles) 17 | settings.startTime = CACurrentMediaTime() 18 | } catch { 19 | fatalError("initialization failed") 20 | } 21 | } 22 | } 23 | 24 | @Published var settings = Settings() 25 | 26 | var particleMeshBuilder: ParticleMeshBuilder 27 | var sceneMeshBuilder: SceneMeshBuilder 28 | var simulator: Simulation 29 | let sceneMeshProvider: SceneMeshProvider 30 | 31 | init() { 32 | sceneMeshBuilder = SceneMeshBuilder() 33 | let image = UIImage(named: "hello_particles")! 34 | selectedImage = image 35 | let correctedImage = image.correctlyOrientedImage() 36 | let (particles, uvs) = Self.buildParticles(for: correctedImage) 37 | do { 38 | sceneMeshProvider = try SceneMeshProvider(device: device) 39 | particleMeshBuilder = try ParticleMeshBuilder( 40 | device: device, 41 | positions: particles, 42 | uvs: uvs, 43 | cellSize: SimulationConstants.cellSize, 44 | size: image.size 45 | ) 46 | simulator = try Simulation(device: device, positions: particles) 47 | } catch { 48 | fatalError("initialization failed") 49 | } 50 | 51 | settings.startTime = CACurrentMediaTime() 52 | sceneMeshProvider.onMeshUpdate = { geometry, event in 53 | DispatchQueue.main.async { [weak self] in 54 | self?.simulator.updateScene(sceneGeometry: geometry, event: event) 55 | Task { 56 | self?.sceneMeshBuilder.updateGeometry(geometry: geometry) 57 | } 58 | } 59 | } 60 | } 61 | 62 | 63 | func setSelectedImage(_ image: UIImage) { 64 | selectedImage = image 65 | } 66 | 67 | func reset() { 68 | sceneMeshProvider.forceUpdate() 69 | simulator.reset() 70 | settings.startTime = CACurrentMediaTime() 71 | objectWillChange.send() 72 | } 73 | } 74 | 75 | extension ViewModel { 76 | private static func buildParticles(for image: UIImage) -> ([SIMD4], [SIMD2]) { 77 | let fixedHeight = 40 78 | let aspectRatio = Float(image.size.width / image.size.height) 79 | let width = Int(ceil(Float(fixedHeight) * aspectRatio)) 80 | let depth = 11 81 | 82 | let count = width * fixedHeight * depth 83 | var positions = [SIMD4](repeating: .zero, count: count) 84 | var uvs = [SIMD2](repeating: .zero, count: count) 85 | 86 | let boxSize: SIMD3 = [aspectRatio, 1.0, 1.0] 87 | let cellSizeX = 1.0 / Float(width) 88 | let cellSizeY = 1.0 / Float(fixedHeight) 89 | 90 | for x in 0..(xPos, yPos, zPos, 1) 102 | let uv = SIMD2(u + cellSizeX * 0.5, v + cellSizeY * 0.5) 103 | 104 | positions[index] = position 105 | uvs[index] = uv 106 | } 107 | } 108 | } 109 | 110 | return (positions, uvs) 111 | } 112 | } 113 | 114 | final class Settings: Observable { 115 | var showWireframe = false 116 | var startTime: TimeInterval = CACurrentMediaTime() 117 | } 118 | 119 | extension UIImage { 120 | func correctlyOrientedImage() -> UIImage { 121 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 122 | draw(in: CGRect(origin: .zero, size: size)) 123 | let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()! 124 | UIGraphicsEndImageContext() 125 | 126 | return normalizedImage 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Simulation/Simulation.metal: -------------------------------------------------------------------------------- 1 | #include "../Utils.h" 2 | 3 | kernel void predictPositions( 4 | device float3 *positions [[buffer(0)]], 5 | device float3 *prevPositions [[buffer(1)]], 6 | device float3 *predictedPositions [[buffer(2)]], 7 | constant float3 &gravity [[buffer(3)]], 8 | constant float &timeStep [[buffer(4)]], 9 | constant uint &positionsCount [[buffer(5)]], 10 | constant float3 &left [[buffer(6)]], 11 | constant float3 &right [[buffer(7)]], 12 | constant float3 &head [[buffer(8)]], 13 | uint id [[thread_position_in_grid]] 14 | ) { 15 | if (id >= positionsCount) { return; } 16 | 17 | float3 position = positions[id]; 18 | float3 prevPosition = prevPositions[id]; 19 | float3 velocity = position - prevPosition; 20 | 21 | const float3 attractor = mix(head, (left + right) / 2.0, 2.0); 22 | float3 toAttractor = attractor - position; 23 | float3 attractorForce = normalize(toAttractor) * smoothstep(0.2, 0.0, length(left - right)) * 12.0; 24 | 25 | const float damping = 0.98; 26 | velocity *= damping; 27 | velocity = min(max(velocity, -0.05), 0.05); 28 | velocity += attractorForce * timeStep * timeStep; 29 | float3 newPosition = position + velocity + gravity * timeStep * timeStep; 30 | 31 | prevPositions[id] = position; 32 | positions[id] = newPosition; 33 | predictedPositions[id] = newPosition; 34 | } 35 | 36 | kernel void solveConstraints( 37 | constant float3 &leftHandCollider [[buffer(0)]], 38 | constant float3 &rightHandCollider [[buffer(1)]], 39 | device uint *collisionCandidates [[buffer(2)]], 40 | device float3 *positions [[buffer(3)]], 41 | device float3 *prevPositions [[buffer(4)]], 42 | constant uint &collisionCandidatesCount [[buffer(5)]], 43 | constant float &particleRadius [[buffer(9)]], 44 | device float3 *targetPositions [[buffer(10)]], 45 | constant uint &positionsCount [[buffer(11)]], 46 | uint gid [[ thread_position_in_grid ]] 47 | ) { 48 | if (gid >= positionsCount) { return; } 49 | uint vertexIndex = gid; 50 | 51 | float3 position = positions[vertexIndex].xyz; 52 | float3 prevPosition = prevPositions[vertexIndex].xyz; 53 | float3 velocity = position - prevPosition; 54 | float3 totalCorrection = 0; 55 | float correctionCount = 0; 56 | 57 | if (position.y < 0) { 58 | float3 direction = float3(0, 1, 0); 59 | float lambda = -position.y; 60 | float3 friction = computeFriction(direction, lambda, velocity, 0.5); 61 | 62 | float3 correction = (lambda * direction + friction) * 0.5; 63 | totalCorrection += correction; 64 | correctionCount += 1.0; 65 | } 66 | 67 | float leftHandDistance = length(leftHandCollider - position); 68 | float rightHandDistance = length(rightHandCollider - position); 69 | 70 | if (leftHandDistance < 0.1) { 71 | float3 direction = normalize(leftHandCollider - position); 72 | float error = leftHandDistance - 0.1; 73 | float3 correction = (error * direction); 74 | totalCorrection += correction; 75 | correctionCount += 1.0; 76 | } 77 | 78 | if (rightHandDistance < 0.1) { 79 | float3 direction = normalize(rightHandDistance - position); 80 | float error = rightHandDistance - 0.1; 81 | float3 correction = (error * direction); 82 | totalCorrection += correction; 83 | correctionCount += 1.0; 84 | } 85 | 86 | for (int i = 0; i < int(collisionCandidatesCount); i++) { 87 | uint collisionVertexIndex = collisionCandidates[gid * collisionCandidatesCount + i]; 88 | if (collisionVertexIndex != vertexIndex && collisionVertexIndex != UINT_MAX) { 89 | float3 neighborPosition = positions[collisionVertexIndex].xyz; 90 | float3 diff = position - neighborPosition; 91 | float diameter2 = particleRadius * 4; 92 | float distance = length_squared(diff); 93 | if (distance < pow(diameter2, 2.0)) { 94 | distance = sqrt(distance); 95 | float3 diff = position - neighborPosition; 96 | float diameter = particleRadius * 2; 97 | float error = distance - diameter; 98 | 99 | float3 prevNeighborPosition = prevPositions[collisionVertexIndex].xyz; 100 | float3 neighborVelocity = neighborPosition - prevNeighborPosition; 101 | if (!isnormal(distance)) { continue; } 102 | float3 direction = diff / distance; 103 | float3 friction = computeFriction(direction, -error, velocity - neighborVelocity, 0.5); 104 | 105 | float relaxation = error < 0 ? 0.125 : 0.0000001; 106 | float3 correction = (-error * direction + friction) * relaxation; 107 | totalCorrection += correction; 108 | correctionCount += 1.0; 109 | } 110 | } 111 | } 112 | 113 | targetPositions[vertexIndex].xyz = position + (totalCorrection / max(1.0, totalCorrection)); 114 | } 115 | 116 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/UI/ImmersiveView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import RealityKit 3 | import RealityKitContent 4 | import PhotosUI 5 | 6 | struct ImmersiveView: View { 7 | private enum Constants { 8 | static let textureParamterName = "texture" 9 | static let timeParameterName = "time" 10 | static let cellSizeParamaterName = "cellSize" 11 | } 12 | 13 | @Environment(AppModel.self) private var appModel 14 | @EnvironmentObject private var viewModel: ViewModel 15 | @State private var particlesEntity: Entity? 16 | private let session = SpatialTrackingSession() 17 | 18 | var body: some View { 19 | RealityView(make: { content in 20 | #if !targetEnvironment(simulator) 21 | _ = await session.run(.init(tracking: [.hand])) 22 | #endif 23 | let rootEntity = Entity() 24 | Task { 25 | var particleMaterial = try await ShaderGraphMaterial(named: "/Root/BillboardMaterial", from: "Immersive", in: realityKitContentBundle) 26 | try await configureParticleMaterial(material: &particleMaterial, startTime: CACurrentMediaTime()) 27 | try updateParticlesModel(rootEntity: rootEntity, material: particleMaterial) 28 | } 29 | 30 | let leftPalm = AnchorEntity(.hand(.left, location: .palm)) 31 | leftPalm.name = "LeftPalm" 32 | let rightPalm = AnchorEntity(.hand(.right, location: .palm)) 33 | rightPalm.name = "RightPalm" 34 | let head = AnchorEntity(.head) 35 | head.name = "Head" 36 | rootEntity.addChild(leftPalm) 37 | rootEntity.addChild(rightPalm) 38 | rootEntity.addChild(head) 39 | 40 | #if !targetEnvironment(simulator) 41 | rootEntity.components[SceneMeshProviderComponent.self] = .init(meshBuilder: viewModel.sceneMeshBuilder) 42 | #endif 43 | rootEntity.components[SettingsComponent.self] = .init(settings: viewModel.settings) 44 | 45 | content.add(rootEntity) 46 | 47 | SimulationSystem.registerSystem() 48 | ParticlesUpdateSystem.registerSystem() 49 | #if !targetEnvironment(simulator) 50 | SceneUpdateSystem.registerSystem() 51 | #endif 52 | }, update: { content in 53 | let model = particlesEntity?.components[ModelComponent.self] 54 | particlesEntity?.components.set(OpacityComponent(opacity: 0.0)) 55 | Task { 56 | if var material = model?.materials.first as? ShaderGraphMaterial, var model { 57 | try? await self.configureParticleMaterial(material: &material, startTime: viewModel.settings.startTime) 58 | model.materials = [material] 59 | particlesEntity?.components[ModelComponent.self] = model 60 | particlesEntity?.components.set(OpacityComponent(opacity: 1.0)) 61 | 62 | if let simulatorComponent = particlesEntity?.components[SimulationComponent.self] { 63 | if let rootEntity = particlesEntity?.parent, simulatorComponent.simulator == nil { 64 | try updateParticlesModel(rootEntity: rootEntity, material: material) 65 | } 66 | } 67 | } 68 | } 69 | }) 70 | .onChange(of: appModel.immersiveSpaceState) { _, _ in 71 | self.viewModel.settings.startTime = CACurrentMediaTime() 72 | } 73 | } 74 | 75 | private func updateParticlesModel(rootEntity: Entity, material: RealityKit.Material) throws { 76 | let meshResource = try viewModel.particleMeshBuilder.createMeshResource() 77 | let particlesEntity = ModelEntity(mesh: meshResource, materials: [material]) 78 | particlesEntity.components[SimulationComponent.self] = .init(simulator: viewModel.simulator) 79 | particlesEntity.components[ParticlesUpdateComponent.self] = .init( 80 | ParticlesUpdateComponent(particleMeshBuilder: viewModel.particleMeshBuilder) 81 | ) 82 | 83 | self.particlesEntity?.removeFromParent() 84 | rootEntity.addChild(particlesEntity) 85 | self.particlesEntity = particlesEntity 86 | } 87 | 88 | private func configureParticleMaterial(material: inout ShaderGraphMaterial, startTime: TimeInterval) async throws { 89 | let correctedImage = viewModel.selectedImage.correctlyOrientedImage() 90 | let texResource = try await TextureResource(image: correctedImage.cgImage!, options: .init(semantic: .color)) 91 | try? material.setParameter(name: Constants.timeParameterName, value: .float(Float(startTime))) 92 | try? material.setParameter(name: Constants.textureParamterName, value: .textureResource(texResource)) 93 | try? material.setParameter(name: Constants.cellSizeParamaterName, value: .float(Float(SimulationConstants.cellSize))) 94 | } 95 | } 96 | 97 | 98 | #Preview(immersionStyle: .mixed) { 99 | ImmersiveView() 100 | .environment(AppModel()) 101 | .environmentObject(ViewModel()) 102 | } 103 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/lakiiinbor.rcuserdata: -------------------------------------------------------------------------------- 1 | { 2 | "advancedEditorSelectedIdentifier" : "ShaderGraphEditorPluginID", 3 | "openSceneRelativePaths" : [ 4 | "Immersive.usda" 5 | ], 6 | "recentIdentifiers" : { 7 | "AF09ED6F-1707-48FD-8720-65B998362C09" : [ 8 | 9 | ] 10 | }, 11 | "sceneCameraHistory" : { 12 | "AF09ED6F-1707-48FD-8720-65B998362C09" : [ 13 | { 14 | "date" : 743449497.199421, 15 | "title" : "Untitled", 16 | "transform" : [ 17 | -0.30034718, 18 | -2.245644e-07, 19 | -0.9538299, 20 | 0, 21 | -0.6761133, 22 | 0.7053688, 23 | 0.21289818, 24 | 0, 25 | 0.6728017, 26 | 0.7088406, 27 | -0.21185574, 28 | 0, 29 | 1.9565905, 30 | 1.3026586, 31 | -0.6841501, 32 | 1 33 | ] 34 | }, 35 | { 36 | "date" : 743449495.514256, 37 | "title" : "Untitled", 38 | "transform" : [ 39 | -0.30034718, 40 | -2.245644e-07, 41 | -0.9538299, 42 | 0, 43 | -0.6761133, 44 | 0.7053688, 45 | 0.21289818, 46 | 0, 47 | 0.6728016, 48 | 0.70884055, 49 | -0.21185571, 50 | 0, 51 | 1.7989547, 52 | 1.1365789, 53 | -0.63451284, 54 | 1 55 | ] 56 | }, 57 | { 58 | "date" : 743445650.000333, 59 | "title" : "Untitled", 60 | "transform" : [ 61 | -0.36677638, 62 | -2.1992936e-07, 63 | -0.9303091, 64 | 0, 65 | -0.6901559, 66 | 0.6705586, 67 | 0.27209538, 68 | 0, 69 | 0.6238267, 70 | 0.7418567, 71 | -0.24594526, 72 | 0, 73 | 1.6970452, 74 | 1.2267116, 75 | -0.7388323, 76 | 1 77 | ] 78 | }, 79 | { 80 | "date" : 743445635.835906, 81 | "title" : "Untitled", 82 | "transform" : [ 83 | -0.392946, 84 | -2.415399e-07, 85 | -0.9195615, 86 | 0, 87 | -0.6775717, 88 | 0.67606485, 89 | 0.28953904, 90 | 0, 91 | 0.6216831, 92 | 0.73684216, 93 | -0.2656572, 94 | 0, 95 | 1.0855639, 96 | 0.78196156, 97 | -0.5093876, 98 | 1 99 | ] 100 | }, 101 | { 102 | "date" : 743445576.354388, 103 | "title" : "Untitled", 104 | "transform" : [ 105 | -0.48596212, 106 | -2.395339e-07, 107 | -0.87398, 108 | 0, 109 | -0.56089497, 110 | 0.7668963, 111 | 0.31187624, 112 | 0, 113 | 0.6702518, 114 | 0.6417711, 115 | -0.37268272, 116 | 0, 117 | 1.1076587, 118 | 0.617968, 119 | -0.6637743, 120 | 1 121 | ] 122 | }, 123 | { 124 | "date" : 743444484.940188, 125 | "title" : "Untitled", 126 | "transform" : [ 127 | -0.7830175, 128 | -2.4674938e-07, 129 | -0.62199974, 130 | 0, 131 | -0.42037454, 132 | 0.7370452, 133 | 0.52919716, 134 | 0, 135 | 0.45844176, 136 | 0.6758435, 137 | -0.5771194, 138 | 0, 139 | 1.2745374, 140 | 1.1456946, 141 | -1.718534, 142 | 1 143 | ] 144 | }, 145 | { 146 | "date" : 743444328.867092, 147 | "title" : "Untitled", 148 | "transform" : [ 149 | -0.7830175, 150 | -2.252324e-07, 151 | -0.6219997, 152 | 0, 153 | -0.42037445, 154 | 0.7370451, 155 | 0.5291972, 156 | 0, 157 | 0.4584418, 158 | 0.67584366, 159 | -0.5771191, 160 | 0, 161 | 1.160296, 162 | 1.7760057, 163 | -1.1034439, 164 | 1 165 | ] 166 | }, 167 | { 168 | "date" : 743444216.781989, 169 | "title" : "Untitled", 170 | "transform" : [ 171 | -0.76026016, 172 | -2.2520796e-07, 173 | -0.6496186, 174 | 0, 175 | -0.29235822, 176 | 0.8930055, 177 | 0.34215185, 178 | 0, 179 | 0.5801132, 180 | 0.45004594, 181 | -0.6789164, 182 | 0, 183 | 1.4738626, 184 | 1.2780889, 185 | -1.3828553, 186 | 1 187 | ] 188 | }, 189 | { 190 | "date" : 743441632.580276, 191 | "title" : "Untitled", 192 | "transform" : [ 193 | -0.5130864, 194 | -2.1608403e-07, 195 | -0.8583369, 196 | 0, 197 | -0.6266195, 198 | 0.68340546, 199 | 0.3745731, 200 | 0, 201 | 0.58659214, 202 | 0.7300392, 203 | -0.3506459, 204 | 0, 205 | 1.0549824, 206 | 1.4728544, 207 | -0.42908505, 208 | 1 209 | ] 210 | }, 211 | { 212 | "date" : 743401170.161768, 213 | "title" : "Untitled", 214 | "transform" : [ 215 | -0.51308644, 216 | -2.1608406e-07, 217 | -0.85833704, 218 | 0, 219 | -0.6266195, 220 | 0.68340546, 221 | 0.3745731, 222 | 0, 223 | 0.58659214, 224 | 0.7300392, 225 | -0.3506459, 226 | 0, 227 | 1.0387927, 228 | 1.8636781, 229 | -0.48001266, 230 | 1 231 | ] 232 | } 233 | ] 234 | }, 235 | "selectedSceneRelativePath" : "Immersive.usda" 236 | } -------------------------------------------------------------------------------- /Examples/Photons/Photons/Particles/ParticleMeshBuilder.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | import Metal 3 | 4 | struct ParticleVertex { 5 | var position: SIMD4 = .zero 6 | var uv: SIMD2 = .zero 7 | var uv2: SIMD2 = .zero 8 | 9 | } 10 | 11 | extension ParticleVertex { 12 | static var vertexAttributes: [LowLevelMesh.Attribute] = [ 13 | .init(semantic: .position, format: .half4, offset: MemoryLayout.offset(of: \.position)!), 14 | .init(semantic: .uv0, format: .float2, offset: MemoryLayout.offset(of: \.uv)!), 15 | .init(semantic: .uv1, format: .float2, offset: MemoryLayout.offset(of: \.uv2)!), 16 | ] 17 | 18 | static var vertexLayouts: [LowLevelMesh.Layout] = [ 19 | .init(bufferIndex: 0, bufferStride: MemoryLayout.stride) 20 | ] 21 | 22 | static var descriptor: LowLevelMesh.Descriptor { 23 | var desc = LowLevelMesh.Descriptor() 24 | desc.vertexAttributes = ParticleVertex.vertexAttributes 25 | desc.vertexLayouts = ParticleVertex.vertexLayouts 26 | desc.indexType = .uint32 27 | return desc 28 | } 29 | } 30 | 31 | final class ParticleMeshBuilder { 32 | private var pipelineState: MTLComputePipelineState 33 | private let device: MTLDevice 34 | private var lowLevelMesh: LowLevelMesh? 35 | private var particlePositions: [SIMD4] 36 | private var uvs: [SIMD2] 37 | private var size: CGSize 38 | private var cellSize: Float 39 | private(set) var uvBuffer: MTLBuffer 40 | 41 | init( 42 | device: MTLDevice, 43 | positions: [SIMD4], 44 | uvs: [SIMD2], 45 | cellSize: Float, 46 | size: CGSize 47 | ) throws { 48 | let library = try device.makeDefaultLibrary(bundle: .main) 49 | self.device = device 50 | self.particlePositions = positions 51 | self.uvs = uvs 52 | self.size = size 53 | self.cellSize = cellSize 54 | uvBuffer = try device.buffer(with: uvs) 55 | pipelineState = try library.computePipelineState(function: "updateVertexBuffer") 56 | } 57 | 58 | @MainActor func createMeshResource() throws -> MeshResource { 59 | let lowLevelMesh = try buildMesh() 60 | let resource = try MeshResource(from: lowLevelMesh) 61 | self.lowLevelMesh = lowLevelMesh 62 | return resource 63 | } 64 | 65 | @MainActor func replaceMeshBuffer( 66 | poisitionsBuffer: MTLBuffer, 67 | vertexNeighbors: MTLBuffer, 68 | commandBuffer: MTLCommandBuffer 69 | ) { 70 | guard let lowLevelMesh else { return } 71 | let vertexBuffer = lowLevelMesh.replace(bufferIndex: 0, using: commandBuffer) 72 | let positionsCount = lowLevelMesh.vertexCapacity 73 | 74 | commandBuffer.compute { encoder in 75 | encoder.setBuffer(vertexBuffer, offset: 0, index: 0) 76 | encoder.setBuffer(poisitionsBuffer, offset: 0, index: 1) 77 | encoder.setValue(UInt32(positionsCount), at: 2) 78 | encoder.setBuffer(uvBuffer, offset: 0, index: 3) 79 | encoder.setValue(Float(size.height / size.width), at: 4) 80 | encoder.setValue(cellSize, at: 5) 81 | encoder.dispatch1d(state: pipelineState, exactlyOrCovering: positionsCount) 82 | } 83 | } 84 | 85 | @MainActor private func buildMesh() throws -> LowLevelMesh { 86 | let halfSize = cellSize / 2 87 | 88 | var positions: [SIMD4] = [] 89 | var indices: [UInt32] = [] 90 | 91 | for particlePosition in particlePositions { 92 | let particlePos = SIMD3(particlePosition.x, particlePosition.y, particlePosition.z) 93 | 94 | let quadVertices: [SIMD4] = [ 95 | SIMD4(particlePos.x - halfSize, particlePos.y - halfSize, particlePos.z - halfSize, 1.0), 96 | SIMD4(particlePos.x + halfSize, particlePos.y - halfSize, particlePos.z - halfSize, 1.0), 97 | SIMD4(particlePos.x - halfSize, particlePos.y + halfSize, particlePos.z - halfSize, 1.0), 98 | SIMD4(particlePos.x + halfSize, particlePos.y + halfSize, particlePos.z - halfSize, 1.0), 99 | ] 100 | 101 | positions.append(contentsOf: quadVertices) 102 | let baseIndex = UInt32(positions.count - 4) 103 | let quadIndices: [UInt32] = (0..<1).flatMap { face -> [UInt32] in 104 | let faceBaseIndex = baseIndex + UInt32(face * 4) 105 | return [ 106 | faceBaseIndex, faceBaseIndex + 1, faceBaseIndex + 2, 107 | faceBaseIndex + 1, faceBaseIndex + 3, faceBaseIndex + 2 108 | ] 109 | } 110 | indices.append(contentsOf: quadIndices) 111 | } 112 | 113 | var desciptor = ParticleVertex.descriptor 114 | desciptor.vertexCapacity = positions.count 115 | desciptor.indexCapacity = indices.count 116 | 117 | let lowMesh = try LowLevelMesh(descriptor: desciptor) 118 | lowMesh.withUnsafeMutableIndices { rawIndices in 119 | let targetIndices = rawIndices.bindMemory(to: UInt32.self) 120 | for i in 0.. { 141 | .init(x: x, y: y, z: z) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Examples/Photons/Photons/Scene/SceneCollisionEncoder.swift: -------------------------------------------------------------------------------- 1 | import Metal 2 | import SimulationTools 3 | 4 | final class SceneCollisionEncoder { 5 | private let backgroundQueue: DispatchQueue = .init(label: "Mesh Processing") 6 | private var triangleNeigbhors: MTLTypedBuffer? 7 | 8 | private let handleSceneCollision: MTLComputePipelineState 9 | private var triangles: MTLTypedBuffer? 10 | private var scenePositions: MTLTypedBuffer? 11 | private var triangleSpatialHashing: TriangleSpatialHashing? 12 | private var initialized = false 13 | 14 | init(device: MTLDevice) throws { 15 | handleSceneCollision = try device.makeDefaultLibrary(bundle: .main).computePipelineState(function: "handleVertexTriangleCollision") 16 | } 17 | 18 | func update(collider: SimpleGeometry, updateTriangleNeigbhors: Bool) throws { 19 | let cellSize: Float = 0.05 20 | let configuration = TriangleSpatialHashing.Configuration(cellSize: cellSize) 21 | triangleSpatialHashing = try .init( 22 | heap: device.heap( 23 | size: TriangleSpatialHashing.totalBuffersSize(maxTrianglesCount: collider.trianglesCount, configuration: configuration), 24 | storageMode: .private 25 | ), 26 | configuration: configuration, 27 | maxTrianglesCount: collider.trianglesCount 28 | ) 29 | 30 | triangles = device.typedBuffer(with: collider.triangles, valueType: .packedUInt3) 31 | scenePositions = device.typedBuffer(with: collider.positions, valueType: .packedFloat3) 32 | 33 | if updateTriangleNeigbhors { 34 | backgroundQueue.async { [weak self] in 35 | if let indices = collider.triangles.array(of: UInt32.self, count: collider.trianglesCount * 3) { 36 | let adjacentTriangles = findAdjacentTriangles(indices: indices) 37 | self?.triangleNeigbhors = try? device.typedBuffer(with: adjacentTriangles, valueType: .uint3) 38 | } 39 | } 40 | } 41 | 42 | initialized = false 43 | } 44 | 45 | func build( 46 | commandBuffer: MTLCommandBuffer, 47 | positions: MTLTypedBuffer, 48 | collisionCandidates: MTLTypedBuffer, 49 | vertexNeighbors: MTLTypedBuffer 50 | ) { 51 | guard let triangles = triangles, let scenePositions = scenePositions, let triangleSpatialHashing else { return } 52 | 53 | if !initialized { 54 | triangleSpatialHashing.build( 55 | colliderPositions: scenePositions, 56 | indices: triangles, 57 | in: commandBuffer 58 | ) 59 | } 60 | 61 | triangleSpatialHashing.find( 62 | collidablePositions: positions, 63 | colliderPositions: scenePositions, 64 | indices: triangles, 65 | collisionCandidates: collisionCandidates, 66 | in: commandBuffer 67 | ) 68 | 69 | initialized = true 70 | } 71 | 72 | func reuse( 73 | commandBuffer: MTLCommandBuffer, 74 | positions: MTLTypedBuffer, 75 | collisionCandidates: MTLTypedBuffer, 76 | vertexNeighbors: MTLTypedBuffer 77 | ) { 78 | guard let triangles = triangles, let scenePositions = scenePositions, let triangleSpatialHashing else { return } 79 | 80 | triangleSpatialHashing.reuse( 81 | collidablePositions: positions, 82 | colliderPositions: scenePositions, 83 | indices: triangles, 84 | collisionCandidates: collisionCandidates, 85 | vertexNeighbors: vertexNeighbors, 86 | trinagleNeighbors: triangleNeigbhors, 87 | in: commandBuffer 88 | ) 89 | } 90 | 91 | func encode( 92 | commandBuffer: MTLCommandBuffer, 93 | positions: MTLTypedBuffer, 94 | prevPositions: MTLTypedBuffer, 95 | collisionCandidates: MTLTypedBuffer, 96 | frictionCoefficent: Float 97 | ) { 98 | guard let triangles = triangles, let scenePositions = scenePositions else { return } 99 | 100 | commandBuffer.compute { encoder in 101 | encoder.setBuffer(positions.buffer, offset: 0, index: 0) 102 | encoder.setBuffer(prevPositions.buffer, offset: 0, index: 1) 103 | encoder.setBuffer(collisionCandidates.buffer, offset: 0, index: 2) 104 | encoder.setBuffer(scenePositions.buffer, offset: 0, index: 3) 105 | encoder.setBuffer(triangles.buffer, offset: 0, index: 4) 106 | encoder.setValue(frictionCoefficent, at: 5) 107 | encoder.setValue(UInt32(collisionCandidates.descriptor.count / positions.descriptor.count), at: 6) 108 | encoder.setValue(UInt32(positions.descriptor.count), at: 7) 109 | 110 | encoder.dispatch1d(state: handleSceneCollision, exactlyOrCovering: positions.descriptor.count) 111 | } 112 | } 113 | } 114 | 115 | 116 | private func findAdjacentTriangles(indices: [UInt32]) -> [SIMD3] { 117 | let triangleCount = indices.count / 3 118 | var edgeToTriangle = [Set: Set]() 119 | 120 | for i in 0..()].insert(UInt32(i)) 133 | } 134 | } 135 | 136 | var result = [SIMD3]() 137 | for i in 0.. = [0, -9.8, 0] 8 | static let particleRadius: Float = 0.025 / 2 9 | static var cellSize: Float { particleRadius * 2.0 } 10 | static let maxCollisionCandidates = 8 11 | static let solverIterations = 1 12 | } 13 | 14 | class Simulation { 15 | let device: MTLDevice 16 | 17 | private let predictPositionsPipelineState: MTLComputePipelineState 18 | private let solveConstraintsPipelineState: MTLComputePipelineState 19 | 20 | private let spatialHashing: SpatialHashing 21 | private let sceneCollisionEngine: SceneCollisionEncoder 22 | 23 | let positions: MTLTypedBuffer 24 | let selfCollisionCandidates: MTLTypedBuffer 25 | private let originalPositions: MTLTypedBuffer 26 | private let prevPositions: MTLTypedBuffer 27 | private let predictedPositions: MTLTypedBuffer 28 | private let collisionCandidates: MTLTypedBuffer 29 | 30 | private var time: Float = 0 31 | private var frame: Int = 0 32 | 33 | init(device: MTLDevice, positions: [SIMD4]) throws { 34 | self.device = device 35 | 36 | self.positions = try device.typedBuffer(with: positions, valueType: .float4) 37 | originalPositions = try device.typedBuffer(with: positions, valueType: .float4) 38 | prevPositions = try device.typedBuffer(with: positions, valueType: .float4) 39 | predictedPositions = try device.typedBuffer(with: positions, valueType: .float4) 40 | selfCollisionCandidates = try device.typedBuffer(descriptor: .init(valueType: .uint, count: positions.count * SimulationConstants.maxCollisionCandidates)) 41 | collisionCandidates = try device.typedBuffer(descriptor: .init(valueType: .uint,count: positions.count * SimulationConstants.maxCollisionCandidates)) 42 | 43 | let library = try device.makeDefaultLibrary(bundle: .main) 44 | predictPositionsPipelineState = try library.computePipelineState(function: "predictPositions") 45 | solveConstraintsPipelineState = try library.computePipelineState(function: "solveConstraints") 46 | 47 | let config = SpatialHashing.Configuration(cellSize: SimulationConstants.cellSize, radius: SimulationConstants.particleRadius) 48 | sceneCollisionEngine = try SceneCollisionEncoder(device: device) 49 | spatialHashing = try SpatialHashing( 50 | heap: device.heap(size: SpatialHashing.totalBuffersSize(maxPositionsCount: positions.count), storageMode: .private), 51 | configuration: config, 52 | maxPositionsCount: positions.count 53 | ) 54 | } 55 | 56 | func updateScene(sceneGeometry: SimpleGeometry, event: AnchorUpdate.Event) { 57 | try? sceneCollisionEngine.update( 58 | collider: sceneGeometry, 59 | updateTriangleNeigbhors: event != .updated 60 | ) 61 | } 62 | 63 | func reset() { 64 | time = 0 65 | frame = 0 66 | } 67 | 68 | func update( 69 | leftHandPosition: SIMD3, 70 | rightHandPosition: SIMD3, 71 | headPosition: SIMD3, 72 | commandBuffer: MTLCommandBuffer, 73 | dt: TimeInterval 74 | ) { 75 | 76 | if frame == 0 && time == 0 { 77 | commandBuffer.blit { encoder in 78 | encoder.copy(from: originalPositions.buffer, sourceOffset: 0, to: positions.buffer, destinationOffset: 0, size: positions.buffer.length) 79 | encoder.copy(from: originalPositions.buffer, sourceOffset: 0, to: prevPositions.buffer, destinationOffset: 0, size: positions.buffer.length) 80 | encoder.copy(from: originalPositions.buffer, sourceOffset: 0, to: predictedPositions.buffer, destinationOffset: 0, size: positions.buffer.length) 81 | 82 | } 83 | } 84 | 85 | frame += 1 86 | let dt = 1.0 / 90 87 | time += Float(dt) 88 | for _ in 0.. 120 { 90 | commandBuffer.compute { predictEncoder in 91 | predictEncoder.setBuffer(positions.buffer, offset: 0, index: 0) 92 | predictEncoder.setBuffer(prevPositions.buffer, offset: 0, index: 1) 93 | predictEncoder.setBuffer(predictedPositions.buffer, offset: 0, index: 2) 94 | predictEncoder.setValue(SIMD3(0, -10, 0.0), at: 3) 95 | predictEncoder.setValue(Float(dt), at: 4) 96 | predictEncoder.setValue(UInt32(positions.descriptor.count), at: 5) 97 | predictEncoder.setValue(leftHandPosition, at: 6) 98 | predictEncoder.setValue(rightHandPosition, at: 7) 99 | predictEncoder.setValue(headPosition, at: 8) 100 | predictEncoder.dispatch1d(state: predictPositionsPipelineState, exactlyOrCovering: positions.descriptor.count) 101 | } 102 | } 103 | 104 | spatialHashing.build( 105 | positions: predictedPositions, 106 | in: commandBuffer 107 | ) 108 | 109 | spatialHashing.find( 110 | collidablePositions: nil, 111 | collisionCandidates: selfCollisionCandidates, 112 | connectedVertices: nil, 113 | in: commandBuffer 114 | ) 115 | 116 | sceneCollisionEngine.build( 117 | commandBuffer: commandBuffer, 118 | positions: predictedPositions, 119 | collisionCandidates: collisionCandidates, 120 | vertexNeighbors: selfCollisionCandidates 121 | ) 122 | 123 | sceneCollisionEngine.reuse( 124 | commandBuffer: commandBuffer, 125 | positions: predictedPositions, 126 | collisionCandidates: collisionCandidates, 127 | vertexNeighbors: selfCollisionCandidates 128 | ) 129 | 130 | for j in 0..<3 { 131 | let source = j % 2 == 0 ? predictedPositions : positions 132 | let target = j % 2 == 0 ? positions : predictedPositions 133 | 134 | commandBuffer.compute { solveEncoder in 135 | solveEncoder.setValue(leftHandPosition, at: 0) 136 | solveEncoder.setValue(rightHandPosition, at: 1) 137 | solveEncoder.setBuffer(selfCollisionCandidates.buffer, offset: 0, index: 2) 138 | solveEncoder.setBuffer(source.buffer, offset: 0, index: 3) 139 | solveEncoder.setBuffer(prevPositions.buffer, offset: 0, index: 4) 140 | solveEncoder.setValue(SimulationConstants.maxCollisionCandidates, at: 5) 141 | solveEncoder.setValue(SimulationConstants.particleRadius, at: 9) 142 | solveEncoder.setBuffer(target.buffer, offset: 0, index: 10) 143 | solveEncoder.setValue(UInt32(positions.descriptor.count), at: 11) 144 | 145 | solveEncoder.dispatch1d(state: solveConstraintsPipelineState, exactlyOrCovering: positions.descriptor.count) 146 | } 147 | 148 | sceneCollisionEngine.encode( 149 | commandBuffer: commandBuffer, 150 | positions: target, 151 | prevPositions: prevPositions, 152 | collisionCandidates: collisionCandidates, 153 | frictionCoefficent: 0.5 154 | ) 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Materials/GridMaterial.usda: -------------------------------------------------------------------------------- 1 | #usda 1.0 2 | ( 3 | defaultPrim = "Root" 4 | metersPerUnit = 1 5 | upAxis = "Y" 6 | ) 7 | 8 | def Xform "Root" 9 | { 10 | def Material "GridMaterial" 11 | { 12 | reorder nameChildren = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "DefaultSurfaceShader", "MaterialXPreviewSurface", "Texcoord", "Add", "Multiply", "Fractional", "LineCounts", "Multiply_1", "Separate2", "Separate2_1", "Ifgreater", "Ifgreater_1", "Max", "Background_Color"] 13 | token outputs:mtlx:surface.connect = 14 | token outputs:realitykit:vertex 15 | token outputs:surface 16 | float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (2222, 300.5) 17 | float2 ui:nodegraph:realitykit:subgraphOutputs:size = (182, 89) 18 | int ui:nodegraph:realitykit:subgraphOutputs:stackingOrder = 749 19 | 20 | def Shader "DefaultSurfaceShader" 21 | { 22 | uniform token info:id = "UsdPreviewSurface" 23 | color3f inputs:diffuseColor = (1, 1, 1) 24 | float inputs:roughness = 0.75 25 | token outputs:surface 26 | } 27 | 28 | def Shader "MaterialXPreviewSurface" 29 | { 30 | uniform token info:id = "ND_UsdPreviewSurface_surfaceshader" 31 | float inputs:clearcoat 32 | float inputs:clearcoatRoughness 33 | color3f inputs:diffuseColor.connect = 34 | color3f inputs:emissiveColor 35 | float inputs:ior 36 | float inputs:metallic = 0.15 37 | float3 inputs:normal 38 | float inputs:occlusion 39 | float inputs:opacity 40 | float inputs:opacityThreshold 41 | float inputs:roughness = 0.5 42 | token outputs:out 43 | float2 ui:nodegraph:node:pos = (1967, 300.5) 44 | float2 ui:nodegraph:node:size = (208, 297) 45 | int ui:nodegraph:node:stackingOrder = 870 46 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["Advanced"] 47 | } 48 | 49 | def Shader "Texcoord" 50 | { 51 | uniform token info:id = "ND_texcoord_vector2" 52 | float2 outputs:out 53 | float2 ui:nodegraph:node:pos = (94.14453, 35.29297) 54 | float2 ui:nodegraph:node:size = (182, 43) 55 | int ui:nodegraph:node:stackingOrder = 1358 56 | } 57 | 58 | def Shader "Multiply" 59 | { 60 | uniform token info:id = "ND_multiply_vector2" 61 | float2 inputs:in1.connect = 62 | float2 inputs:in2 = (32, 15) 63 | float2 inputs:in2.connect = 64 | float2 outputs:out 65 | float2 ui:nodegraph:node:pos = (275.64453, 47.29297) 66 | float2 ui:nodegraph:node:size = (61, 36) 67 | int ui:nodegraph:node:stackingOrder = 1348 68 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:in2"] 69 | } 70 | 71 | def Shader "Fractional" 72 | { 73 | uniform token info:id = "ND_realitykit_fractional_vector2" 74 | float2 inputs:in.connect = 75 | float2 outputs:out 76 | float2 ui:nodegraph:node:pos = (440.5, 49.5) 77 | float2 ui:nodegraph:node:size = (155, 99) 78 | int ui:nodegraph:node:stackingOrder = 1345 79 | } 80 | 81 | def Shader "BaseColor" 82 | { 83 | uniform token info:id = "ND_constant_color3" 84 | color3f inputs:value = (0.89737034, 0.89737034, 0.89737034) ( 85 | colorSpace = "Input - Texture - sRGB - sRGB" 86 | ) 87 | color3f inputs:value.connect = None 88 | color3f outputs:out 89 | float2 ui:nodegraph:node:pos = (1537.5977, 363.07812) 90 | float2 ui:nodegraph:node:size = (150, 43) 91 | int ui:nodegraph:node:stackingOrder = 1353 92 | } 93 | 94 | def Shader "LineColor" 95 | { 96 | uniform token info:id = "ND_constant_color3" 97 | color3f inputs:value = (0.55945957, 0.55945957, 0.55945957) ( 98 | colorSpace = "Input - Texture - sRGB - sRGB" 99 | ) 100 | color3f inputs:value.connect = None 101 | color3f outputs:out 102 | float2 ui:nodegraph:node:pos = (1536.9844, 287.86328) 103 | float2 ui:nodegraph:node:size = (146, 43) 104 | int ui:nodegraph:node:stackingOrder = 1355 105 | } 106 | 107 | def Shader "LineWidths" 108 | { 109 | uniform token info:id = "ND_combine2_vector2" 110 | float inputs:in1 = 0.1 111 | float inputs:in2 = 0.1 112 | float2 outputs:out 113 | float2 ui:nodegraph:node:pos = (443.64453, 233.79297) 114 | float2 ui:nodegraph:node:size = (151, 43) 115 | int ui:nodegraph:node:stackingOrder = 1361 116 | } 117 | 118 | def Shader "LineCounts" 119 | { 120 | uniform token info:id = "ND_combine2_vector2" 121 | float inputs:in1 = 24 122 | float inputs:in2 = 12 123 | float2 outputs:out 124 | float2 ui:nodegraph:node:pos = (94.14453, 138.29297) 125 | float2 ui:nodegraph:node:size = (153, 43) 126 | int ui:nodegraph:node:stackingOrder = 1359 127 | } 128 | 129 | def Shader "Remap" 130 | { 131 | uniform token info:id = "ND_remap_color3" 132 | color3f inputs:in.connect = 133 | color3f inputs:inhigh.connect = None 134 | color3f inputs:inlow.connect = None 135 | color3f inputs:outhigh.connect = 136 | color3f inputs:outlow.connect = 137 | color3f outputs:out 138 | float2 ui:nodegraph:node:pos = (1755.5, 300.5) 139 | float2 ui:nodegraph:node:size = (95, 171) 140 | int ui:nodegraph:node:stackingOrder = 1282 141 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:outlow"] 142 | } 143 | 144 | def Shader "Separate2" 145 | { 146 | uniform token info:id = "ND_separate2_vector2" 147 | float2 inputs:in.connect = 148 | float outputs:outx 149 | float outputs:outy 150 | float2 ui:nodegraph:node:pos = (1212.6445, 128.91797) 151 | float2 ui:nodegraph:node:size = (116, 117) 152 | int ui:nodegraph:node:stackingOrder = 1363 153 | } 154 | 155 | def Shader "Combine3" 156 | { 157 | uniform token info:id = "ND_combine3_color3" 158 | float inputs:in1.connect = 159 | float inputs:in2.connect = 160 | float inputs:in3.connect = 161 | color3f outputs:out 162 | float2 ui:nodegraph:node:pos = (1578.1445, 128.91797) 163 | float2 ui:nodegraph:node:size = (146, 54) 164 | int ui:nodegraph:node:stackingOrder = 1348 165 | } 166 | 167 | def Shader "Range" 168 | { 169 | uniform token info:id = "ND_range_vector2" 170 | bool inputs:doclamp = 1 171 | float2 inputs:gamma = (2, 2) 172 | float2 inputs:in.connect = 173 | float2 inputs:inhigh.connect = 174 | float2 inputs:inlow = (0.02, 0.02) 175 | float2 inputs:outhigh 176 | float2 inputs:outlow 177 | float2 outputs:out 178 | float2 ui:nodegraph:node:pos = (990.64453, 128.91797) 179 | float2 ui:nodegraph:node:size = (98, 207) 180 | int ui:nodegraph:node:stackingOrder = 1364 181 | } 182 | 183 | def Shader "Subtract" 184 | { 185 | uniform token info:id = "ND_subtract_vector2" 186 | float2 inputs:in1.connect = 187 | float2 inputs:in2.connect = 188 | float2 outputs:out 189 | float2 ui:nodegraph:node:pos = (612.64453, 87.04297) 190 | float2 ui:nodegraph:node:size = (63, 36) 191 | int ui:nodegraph:node:stackingOrder = 1348 192 | } 193 | 194 | def Shader "Absval" 195 | { 196 | uniform token info:id = "ND_absval_vector2" 197 | float2 inputs:in.connect = 198 | float2 outputs:out 199 | float2 ui:nodegraph:node:pos = (765.64453, 87.04297) 200 | float2 ui:nodegraph:node:size = (123, 43) 201 | int ui:nodegraph:node:stackingOrder = 1348 202 | } 203 | 204 | def Shader "Min" 205 | { 206 | uniform token info:id = "ND_min_float" 207 | float inputs:in1.connect = 208 | float inputs:in2.connect = 209 | float outputs:out 210 | float2 ui:nodegraph:node:pos = (1388.1445, 128.91797) 211 | float2 ui:nodegraph:node:size = (114, 36) 212 | int ui:nodegraph:node:stackingOrder = 1363 213 | } 214 | } 215 | } 216 | 217 | -------------------------------------------------------------------------------- /Examples/Photons/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda: -------------------------------------------------------------------------------- 1 | #usda 1.0 2 | ( 3 | defaultPrim = "Root" 4 | metersPerUnit = 1 5 | upAxis = "Y" 6 | ) 7 | 8 | def Xform "Root" ( 9 | prepend apiSchemas = ["MaterialBindingAPI"] 10 | ) 11 | { 12 | reorder nameChildren = ["BillboardMaterial", "SpecialOcclusionMaterial", "Plane"] 13 | rel material:binding = ( 14 | bindMaterialAs = "weakerThanDescendants" 15 | ) 16 | 17 | def Material "BillboardMaterial" 18 | { 19 | reorder nameChildren = ["PreviewSurface", "Image", "GeometryModifier", "Position", "Multiply_2", "ViewFrame", "ParticleSpacePosition", "Add", "Multiply", "Multiply_1", "Separate2"] 20 | float inputs:cellSize = 0 ( 21 | customData = { 22 | dictionary realitykit = { 23 | float2 positionInSubgraph = (2149.7761, 375.67697) 24 | int stackingOrderInSubgraph = 5472 25 | } 26 | } 27 | ) 28 | asset inputs:texture ( 29 | customData = { 30 | dictionary realitykit = { 31 | float2 positionInSubgraph = (1099.3767, -577.86914) 32 | float2 sizeInSubgraph = (115.5, 53) 33 | int stackingOrderInSubgraph = 5451 34 | } 35 | } 36 | ) 37 | float inputs:time = 0 ( 38 | customData = { 39 | dictionary realitykit = { 40 | float2 positionInSubgraph = (439.31454, 525.38214) 41 | float2 sizeInSubgraph = (98, 53) 42 | int stackingOrderInSubgraph = 5416 43 | } 44 | } 45 | ) 46 | token outputs:mtlx:surface.connect = 47 | token outputs:realitykit:vertex.connect = 48 | float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (3395.3894, 294.2436) 49 | float2 ui:nodegraph:realitykit:subgraphOutputs:size = (181.5, 99) 50 | int ui:nodegraph:realitykit:subgraphOutputs:stackingOrder = 5410 51 | 52 | def Shader "Multiply_2" ( 53 | references = None 54 | ) 55 | { 56 | uniform token info:id = "ND_multiply_vector3FA" 57 | float3 inputs:in1.connect = 58 | float inputs:in2 = 0.025 59 | float inputs:in2.connect = 60 | float3 outputs:out 61 | float2 ui:nodegraph:node:pos = (2468.3135, 371.76535) 62 | float2 ui:nodegraph:node:size = (60, 36) 63 | int ui:nodegraph:node:stackingOrder = 5469 64 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:out"] 65 | } 66 | 67 | def NodeGraph "ParticleSpacePosition" ( 68 | references = None 69 | ) 70 | { 71 | float2 outputs:Position ( 72 | customData = { 73 | dictionary realitykit = { 74 | int stackingOrderInSubgraph = 289 75 | } 76 | } 77 | ) 78 | float2 outputs:Position.connect = 79 | float2 ui:nodegraph:node:pos = (-13.930693, 871.25244) 80 | float2 ui:nodegraph:node:size = (187.5, 81) 81 | int ui:nodegraph:node:stackingOrder = 4890 82 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:Position", "outputs:Position", "outputs:Position"] 83 | float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (890.64594, 77.62481) 84 | float2 ui:nodegraph:realitykit:subgraphOutputs:size = (191.0062, 81) 85 | int ui:nodegraph:realitykit:subgraphOutputs:stackingOrder = 588 86 | 87 | def Shader "PositionInParticle" ( 88 | references = None 89 | ) 90 | { 91 | uniform token info:id = "ND_texcoord_vector2" 92 | int inputs:index = 1 93 | float2 outputs:out 94 | float2 ui:nodegraph:node:pos = (106.57422, -31.613281) 95 | float2 ui:nodegraph:node:size = (193.5, 53) 96 | int ui:nodegraph:node:stackingOrder = 594 97 | } 98 | 99 | def Shader "Subtract" ( 100 | references = None 101 | ) 102 | { 103 | uniform token info:id = "ND_subtract_vector2" 104 | float2 inputs:in1.connect = 105 | float2 inputs:in2 = (0.5, 0.5) 106 | float2 outputs:out 107 | float2 ui:nodegraph:node:pos = (325.9453, -23.558594) 108 | float2 ui:nodegraph:node:size = (61.5, 36) 109 | int ui:nodegraph:node:stackingOrder = 590 110 | } 111 | 112 | def Shader "Multiply" 113 | { 114 | uniform token info:id = "ND_multiply_vector2FA" 115 | prepend float2 inputs:in1.connect = 116 | float inputs:in2 = 1 117 | float2 outputs:out 118 | float2 ui:nodegraph:node:pos = (523.59125, 155.27226) 119 | int ui:nodegraph:node:stackingOrder = 587 120 | } 121 | } 122 | 123 | def Shader "Multiply" 124 | { 125 | uniform token info:id = "ND_multiply_vector3FA" 126 | float3 inputs:in1.connect = 127 | float inputs:in2.connect = 128 | float3 outputs:out 129 | float2 ui:nodegraph:node:pos = (2235.056, 939.4348) 130 | float2 ui:nodegraph:node:size = (60, 36) 131 | int ui:nodegraph:node:stackingOrder = 5355 132 | } 133 | 134 | def Shader "Multiply_1" ( 135 | references = None 136 | ) 137 | { 138 | uniform token info:id = "ND_multiply_vector3FA" 139 | float3 inputs:in1.connect = 140 | float inputs:in2.connect = 141 | float3 outputs:out 142 | float2 ui:nodegraph:node:pos = (2206.4163, 1010.0793) 143 | float2 ui:nodegraph:node:size = (60, 36) 144 | int ui:nodegraph:node:stackingOrder = 5357 145 | } 146 | 147 | def Shader "Separate2" ( 148 | references = None 149 | ) 150 | { 151 | uniform token info:id = "ND_separate2_vector2" 152 | float2 inputs:in.connect = 153 | float outputs:outx 154 | float outputs:outy 155 | float2 ui:nodegraph:node:pos = (1129.5726, 1014.8174) 156 | float2 ui:nodegraph:node:size = (115, 127) 157 | int ui:nodegraph:node:stackingOrder = 5371 158 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = [] 159 | } 160 | 161 | def NodeGraph "ViewFrame" ( 162 | references = None 163 | ) 164 | { 165 | float3 inputs:viewDirection = (0, 0, 1) ( 166 | customData = { 167 | dictionary realitykit = { 168 | float2 positionInSubgraph = (-120.11611, 143.32283) 169 | int stackingOrderInSubgraph = 702 170 | } 171 | } 172 | ) 173 | float3 inputs:viewDirection.connect = 174 | float3 outputs:Right ( 175 | customData = { 176 | dictionary realitykit = { 177 | int stackingOrderInSubgraph = 290 178 | } 179 | } 180 | ) 181 | float3 outputs:Right.connect = 182 | float3 outputs:Up ( 183 | customData = { 184 | dictionary realitykit = { 185 | int stackingOrderInSubgraph = 290 186 | } 187 | } 188 | ) 189 | float3 outputs:Up.connect = 190 | float2 ui:nodegraph:node:pos = (2018.7109, 1256.9489) 191 | float2 ui:nodegraph:node:size = (121, 99) 192 | int ui:nodegraph:node:stackingOrder = 5311 193 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:Right"] 194 | float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (1136.9006, -5.6961317) 195 | float2 ui:nodegraph:realitykit:subgraphOutputs:size = (179.01637, 99) 196 | int ui:nodegraph:realitykit:subgraphOutputs:stackingOrder = 661 197 | 198 | def Shader "Right" ( 199 | references = None 200 | ) 201 | { 202 | uniform token info:id = "ND_normalize_vector3" 203 | float3 inputs:in.connect = 204 | float3 outputs:out 205 | float2 ui:nodegraph:node:pos = (507.14062, 16.460938) 206 | float2 ui:nodegraph:node:size = (132.5, 53) 207 | int ui:nodegraph:node:stackingOrder = 511 208 | } 209 | 210 | def Shader "Right_Unnormalized" ( 211 | references = None 212 | ) 213 | { 214 | uniform token info:id = "ND_crossproduct_vector3" 215 | float3 inputs:in1.connect = 216 | float3 inputs:in2 = (0, 1, 0) 217 | float3 outputs:out 218 | float2 ui:nodegraph:node:pos = (285.48438, 16.785156) 219 | float2 ui:nodegraph:node:size = (209.5, 53) 220 | int ui:nodegraph:node:stackingOrder = 661 221 | } 222 | 223 | def Shader "Up_Unnormalized" ( 224 | references = None 225 | ) 226 | { 227 | uniform token info:id = "ND_crossproduct_vector3" 228 | float3 inputs:in1.connect = 229 | float3 inputs:in2.connect = 230 | float3 outputs:out 231 | float2 ui:nodegraph:node:pos = (628.29297, 299.8164) 232 | float2 ui:nodegraph:node:size = (194, 53) 233 | int ui:nodegraph:node:stackingOrder = 698 234 | } 235 | 236 | def Shader "Up" ( 237 | references = None 238 | ) 239 | { 240 | uniform token info:id = "ND_normalize_vector3" 241 | float3 inputs:in.connect = 242 | float3 outputs:out 243 | float2 ui:nodegraph:node:pos = (870.33984, 308.1875) 244 | float2 ui:nodegraph:node:size = (132.5, 53) 245 | int ui:nodegraph:node:stackingOrder = 698 246 | } 247 | 248 | def Shader "Dot" ( 249 | references = None 250 | ) 251 | { 252 | uniform token info:id = "ND_dot_vector3" 253 | float3 inputs:in.connect = 254 | float3 outputs:out 255 | float2 ui:nodegraph:node:pos = (389.49548, 193.04463) 256 | float2 ui:nodegraph:node:size = (44, 18) 257 | int ui:nodegraph:node:stackingOrder = 700 258 | } 259 | 260 | def Scope "Group" ( 261 | kind = "group" 262 | ) 263 | { 264 | string ui:group:annotation = "Compute Left/Right Direction" 265 | string ui:group:annotationDescription = "Each particle is rendered as a plane, and this plane is perpendicular to ViewDirection. The left/right direction is parallel to the plane." 266 | string[] ui:group:members = ["p:Right_Unnormalized", "p:Right"] 267 | } 268 | 269 | def Shader "Dot_1" ( 270 | references = None 271 | ) 272 | { 273 | uniform token info:id = "ND_dot_vector3" 274 | float3 inputs:in.connect = 275 | float3 outputs:out 276 | float2 ui:nodegraph:node:pos = (45.382812, 11.4140625) 277 | float2 ui:nodegraph:node:size = (44, 18) 278 | int ui:nodegraph:node:stackingOrder = 532 279 | } 280 | 281 | def Scope "Group2" ( 282 | kind = "group" 283 | ) 284 | { 285 | string ui:group:annotation = "Compute Up/Down Direction" 286 | string ui:group:annotationDescription = "The up/down direction is parallel to the particle's plane and perpendicular to both ViewDirection and the left/right direction." 287 | string[] ui:group:members = ["p:Up", "p:Up_Unnormalized"] 288 | } 289 | } 290 | 291 | def Shader "Image" 292 | { 293 | uniform token info:id = "ND_image_color3" 294 | color3f inputs:default 295 | prepend asset inputs:file.connect = 296 | string inputs:filtertype 297 | float2 inputs:texcoord 298 | string inputs:uaddressmode 299 | string inputs:vaddressmode 300 | color3f outputs:out 301 | float2 ui:nodegraph:node:pos = (1549.6389, -678.7433) 302 | float2 ui:nodegraph:node:size = (148.5, 199) 303 | int ui:nodegraph:node:stackingOrder = 5460 304 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:file"] 305 | } 306 | 307 | def Shader "Magnitude" 308 | { 309 | uniform token info:id = "ND_magnitude_vector2" 310 | float2 inputs:in.connect = 311 | float outputs:out 312 | float2 ui:nodegraph:node:pos = (1417.497, 13.415811) 313 | float2 ui:nodegraph:node:size = (152, 40) 314 | int ui:nodegraph:node:stackingOrder = 5261 315 | } 316 | 317 | def Shader "SmoothStep_1" 318 | { 319 | uniform token info:id = "ND_smoothstep_float" 320 | float inputs:high = 3 321 | float inputs:in.connect = 322 | float inputs:low = 2 323 | float outputs:out 324 | float2 ui:nodegraph:node:pos = (1163.9985, 238.01328) 325 | float2 ui:nodegraph:node:size = (139.5, 145) 326 | int ui:nodegraph:node:stackingOrder = 5434 327 | } 328 | 329 | def Shader "Mix" 330 | { 331 | uniform token info:id = "ND_mix_vector3" 332 | float3 inputs:bg = (0, 0, 1) 333 | float3 inputs:bg.connect = None 334 | float3 inputs:fg.connect = 335 | float inputs:mix = 0.15 336 | float inputs:mix.connect = 337 | float3 outputs:out 338 | float2 ui:nodegraph:node:pos = (2163.7705, -235.07655) 339 | float2 ui:nodegraph:node:size = (109, 145) 340 | int ui:nodegraph:node:stackingOrder = 5115 341 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:fg", "inputs:fg", "inputs:bg", "inputs:fg", "inputs:mix"] 342 | } 343 | 344 | def Shader "Mix_2" 345 | { 346 | uniform token info:id = "ND_mix_float" 347 | float inputs:bg = 3 348 | float inputs:fg = 0.5 349 | float inputs:mix.connect = 350 | float outputs:out 351 | float2 ui:nodegraph:node:pos = (1392.2803, 151.47961) 352 | float2 ui:nodegraph:node:size = (109, 145) 353 | int ui:nodegraph:node:stackingOrder = 5431 354 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:fg", "inputs:fg", "inputs:fg", "inputs:fg", "inputs:bg", "inputs:bg", "inputs:bg", "inputs:fg"] 355 | } 356 | 357 | def Shader "Subtract_1" 358 | { 359 | uniform token info:id = "ND_subtract_float" 360 | float inputs:in1.connect = 361 | float inputs:in2.connect = 362 | float outputs:out 363 | float2 ui:nodegraph:node:pos = (602.3328, 436.18283) 364 | float2 ui:nodegraph:node:size = (61.5, 36) 365 | int ui:nodegraph:node:stackingOrder = 3246 366 | } 367 | 368 | def Shader "Multiply_3" 369 | { 370 | uniform token info:id = "ND_multiply_float" 371 | float inputs:in1.connect = 372 | float inputs:in2 = 1 373 | float outputs:out 374 | float2 ui:nodegraph:node:pos = (854.7252, 468.61627) 375 | float2 ui:nodegraph:node:size = (60, 36) 376 | int ui:nodegraph:node:stackingOrder = 5272 377 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:in2", "outputs:out"] 378 | } 379 | 380 | def Shader "SmoothStep_2" 381 | { 382 | uniform token info:id = "ND_smoothstep_float" 383 | float inputs:high = 4 384 | float inputs:in.connect = 385 | float inputs:low = 2 386 | float outputs:out 387 | float2 ui:nodegraph:node:pos = (1265.2583, 525.5995) 388 | float2 ui:nodegraph:node:size = (143, 145) 389 | int ui:nodegraph:node:stackingOrder = 5291 390 | } 391 | 392 | def Shader "Multiply_8" 393 | { 394 | uniform token info:id = "ND_multiply_float" 395 | float inputs:in1.connect = 396 | float inputs:in2 = 1 397 | float outputs:out 398 | float2 ui:nodegraph:node:pos = (2383.7788, 81.88629) 399 | float2 ui:nodegraph:node:size = (60, 36) 400 | int ui:nodegraph:node:stackingOrder = 4095 401 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:in1"] 402 | } 403 | 404 | def Shader "Add" ( 405 | references = None 406 | ) 407 | { 408 | uniform token info:id = "ND_add_vector3" 409 | float3 inputs:in1.connect = 410 | float3 inputs:in2.connect = 411 | float3 outputs:out 412 | float2 ui:nodegraph:node:pos = (2375.3484, 955.3678) 413 | float2 ui:nodegraph:node:size = (61.5, 36.5) 414 | int ui:nodegraph:node:stackingOrder = 5348 415 | } 416 | 417 | def Shader "SmoothStep" 418 | { 419 | uniform token info:id = "ND_smoothstep_float" 420 | float inputs:high = 0.49 421 | float inputs:high.connect = None 422 | float inputs:in.connect = 423 | float inputs:low = 0.49 424 | float inputs:low.connect = 425 | float outputs:out 426 | float2 ui:nodegraph:node:pos = (1763.4398, -58.636993) 427 | float2 ui:nodegraph:node:size = (127.5, 145) 428 | int ui:nodegraph:node:stackingOrder = 5259 429 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:low", "inputs:high", "inputs:low", "inputs:in", "inputs:low", "inputs:low", "outputs:out"] 430 | } 431 | 432 | def Shader "ViewDirection" 433 | { 434 | uniform token info:id = "ND_realitykit_viewdirection_vector3" 435 | string inputs:space 436 | float3 outputs:out 437 | float2 ui:nodegraph:node:pos = (1574.1215, 584.7081) 438 | int ui:nodegraph:node:stackingOrder = 5406 439 | } 440 | 441 | def Shader "Mix_4" 442 | { 443 | uniform token info:id = "ND_mix_vector3" 444 | float3 inputs:bg = (0, 0, 1) 445 | float3 inputs:bg.connect = None 446 | float3 inputs:fg = (0, 0, 1) 447 | float3 inputs:fg.connect = 448 | float inputs:mix = 0 449 | float inputs:mix.connect = 450 | float3 outputs:out 451 | float2 ui:nodegraph:node:pos = (1817.2855, 746.1274) 452 | float2 ui:nodegraph:node:size = (109, 145) 453 | int ui:nodegraph:node:stackingOrder = 5402 454 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:fg", "inputs:fg", "inputs:bg", "inputs:fg", "inputs:mix"] 455 | } 456 | 457 | def Shader "PBRSurface" 458 | { 459 | uniform token info:id = "ND_realitykit_pbr_surfaceshader" 460 | float inputs:ambientOcclusion = 1 461 | float inputs:ambientOcclusion.connect = None 462 | color3f inputs:baseColor = (1, 1, 1) ( 463 | colorSpace = "srgb_displayp3" 464 | ) 465 | color3f inputs:baseColor.connect = 466 | float inputs:clearcoat = 0.25 467 | float3 inputs:clearcoatNormal.connect = 468 | float inputs:clearcoatRoughness = 0.125 469 | color3f inputs:emissiveColor.connect = 470 | bool inputs:hasPremultipliedAlpha = 1 471 | float inputs:metallic = 0 472 | float3 inputs:normal.connect = 473 | float inputs:opacity.connect = 474 | float inputs:opacityThreshold = 0.2 475 | float inputs:roughness = 0.3 476 | float inputs:specular = 0.05 477 | token outputs:out 478 | float2 ui:nodegraph:node:pos = (3002.358, -396.1155) 479 | int ui:nodegraph:node:stackingOrder = 5476 480 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:metallic", "inputs:specular", "inputs:ambientOcclusion", "inputs:clearcoatRoughness", "inputs:opacity", "inputs:ambientOcclusion", "inputs:normal", "inputs:ambientOcclusion", "inputs:ambientOcclusion"] 481 | } 482 | 483 | def Shader "GeometryModifier_1" 484 | { 485 | uniform token info:id = "ND_realitykit_geometrymodifier_2_0_vertexshader" 486 | float3 inputs:bitangent.connect = 487 | color4f inputs:color 488 | float3 inputs:modelPositionOffset.connect = 489 | float3 inputs:normal.connect = 490 | float2 inputs:uv0 491 | float2 inputs:uv1 492 | float4 inputs:uv2 493 | float4 inputs:uv3 494 | float4 inputs:uv4.connect = None 495 | float4 inputs:uv5 496 | float4 inputs:uv6 497 | float4 inputs:uv7 498 | token outputs:out 499 | float2 ui:nodegraph:node:pos = (3060.0576, 872.93506) 500 | int ui:nodegraph:node:stackingOrder = 5423 501 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:modelPositionOffset", "inputs:bitangent"] 502 | } 503 | 504 | def Shader "Subtract" 505 | { 506 | uniform token info:id = "ND_subtract_float" 507 | float inputs:in1 = 0.25 508 | float inputs:in2.connect = 509 | float outputs:out 510 | float2 ui:nodegraph:node:pos = (671, 849.5) 511 | int ui:nodegraph:node:stackingOrder = 4944 512 | } 513 | 514 | def Shader "Sqrt" 515 | { 516 | uniform token info:id = "ND_sqrt_float" 517 | float inputs:in.connect = 518 | float outputs:out 519 | float2 ui:nodegraph:node:pos = (996.5, 849.5) 520 | int ui:nodegraph:node:stackingOrder = 4944 521 | } 522 | 523 | def Shader "Clamp" 524 | { 525 | uniform token info:id = "ND_clamp_float" 526 | float inputs:high.connect = None 527 | float inputs:in.connect = 528 | float inputs:low 529 | float outputs:out 530 | float2 ui:nodegraph:node:pos = (819.6384, 721.6268) 531 | int ui:nodegraph:node:stackingOrder = 4944 532 | } 533 | 534 | def Shader "DotProduct" 535 | { 536 | uniform token info:id = "ND_dotproduct_vector2" 537 | float2 inputs:in1.connect = 538 | float2 inputs:in2.connect = 539 | float outputs:out 540 | float2 ui:nodegraph:node:pos = (363.75, 849.5) 541 | float2 ui:nodegraph:node:size = (153.5, 53) 542 | int ui:nodegraph:node:stackingOrder = 4944 543 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:out"] 544 | } 545 | 546 | def Shader "Combine3" 547 | { 548 | uniform token info:id = "ND_combine3_vector3" 549 | float inputs:in1.connect = 550 | float inputs:in2.connect = 551 | float inputs:in3 = 1 552 | float inputs:in3.connect = 553 | float3 outputs:out 554 | float2 ui:nodegraph:node:pos = (1198.202, 746.1572) 555 | float2 ui:nodegraph:node:size = (145, 54) 556 | int ui:nodegraph:node:stackingOrder = 5404 557 | } 558 | 559 | def Shader "Time" 560 | { 561 | uniform token info:id = "ND_time_float" 562 | float outputs:out 563 | float2 ui:nodegraph:node:pos = (380.28607, 338.9291) 564 | float2 ui:nodegraph:node:size = (122.5, 53) 565 | int ui:nodegraph:node:stackingOrder = 5414 566 | } 567 | 568 | def Shader "Mix_3" 569 | { 570 | uniform token info:id = "ND_mix_color3" 571 | color3f inputs:bg.connect = 572 | color3f inputs:fg.connect = 573 | prepend float inputs:mix.connect = 574 | color3f outputs:out 575 | float2 ui:nodegraph:node:pos = (1805.707, -407.1063) 576 | float2 ui:nodegraph:node:size = (109, 145) 577 | int ui:nodegraph:node:stackingOrder = 5477 578 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:fg", "inputs:fg", "inputs:fg", "inputs:fg", "inputs:bg", "inputs:bg", "inputs:bg", "inputs:fg"] 579 | } 580 | 581 | def Shader "Multiply_4" 582 | { 583 | uniform token info:id = "ND_multiply_color3" 584 | color3f inputs:in1.connect = 585 | color3f inputs:in2 = (0.3, 0.3, 0.3) ( 586 | colorSpace = "lin_srgb" 587 | ) 588 | color3f outputs:out 589 | float2 ui:nodegraph:node:pos = (1669.4453, -497.61078) 590 | int ui:nodegraph:node:stackingOrder = 5478 591 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:out"] 592 | } 593 | 594 | def Shader "Multiply_5" 595 | { 596 | uniform token info:id = "ND_multiply_float" 597 | float inputs:in1.connect = 598 | float inputs:in2 = 1.05 599 | float outputs:out 600 | float2 ui:nodegraph:node:pos = (2298.4526, 416.6788) 601 | float2 ui:nodegraph:node:size = (60, 36) 602 | int ui:nodegraph:node:stackingOrder = 5475 603 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:out"] 604 | } 605 | 606 | def Shader "Multiply_6" 607 | { 608 | uniform token info:id = "ND_multiply_color3" 609 | color3f inputs:in1.connect = 610 | color3f inputs:in2 = (0.15, 0.15, 0.15) ( 611 | colorSpace = "lin_srgb" 612 | ) 613 | color3f outputs:out 614 | float2 ui:nodegraph:node:pos = (1555.8214, -447.86472) 615 | int ui:nodegraph:node:stackingOrder = 5482 616 | string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["outputs:out"] 617 | } 618 | } 619 | 620 | def "Plane" ( 621 | active = true 622 | prepend apiSchemas = ["MaterialBindingAPI"] 623 | customData = { 624 | float3 rotationEulerHint = (-1.5707964, 0, 0) 625 | } 626 | references = @plane.usdc@ 627 | ) 628 | { 629 | rel material:binding = ( 630 | bindMaterialAs = "weakerThanDescendants" 631 | ) 632 | quatf xformOp:orient = (0.70710677, -0.70710677, 0, 0) 633 | float3 xformOp:scale = (1, 1, 1) 634 | float3 xformOp:translate = (0, 0, 0) 635 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] 636 | } 637 | } 638 | 639 | -------------------------------------------------------------------------------- /Examples/Photons/Photons.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 60; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D00F9AC12C4AD80F00E5DD8D /* SceneCollisionEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00F9AC02C4AD80F00E5DD8D /* SceneCollisionEncoder.swift */; }; 11 | D00F9AC32C4AD81500E5DD8D /* SceneCollision.metal in Sources */ = {isa = PBXBuildFile; fileRef = D00F9AC22C4AD81500E5DD8D /* SceneCollision.metal */; }; 12 | D0326DFC2C4AE73A008879DD /* ParticleMeshBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0326DFB2C4AE73A008879DD /* ParticleMeshBuilder.swift */; }; 13 | D0326DFE2C4AE789008879DD /* ParticleMeshUpdater.metal in Sources */ = {isa = PBXBuildFile; fileRef = D0326DFD2C4AE789008879DD /* ParticleMeshUpdater.metal */; }; 14 | D03323272C4B04170051640A /* Simulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03323262C4B04170051640A /* Simulation.swift */; }; 15 | D03323292C4B04250051640A /* Simulation.metal in Sources */ = {isa = PBXBuildFile; fileRef = D03323282C4B04250051640A /* Simulation.metal */; }; 16 | D046870B2C62C92800BB6557 /* RealityKitContent in Frameworks */ = {isa = PBXBuildFile; productRef = D07E26982C4AD09A00A3770F /* RealityKitContent */; }; 17 | D07E269B2C4AD09A00A3770F /* PhotonsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E269A2C4AD09A00A3770F /* PhotonsApp.swift */; }; 18 | D07E269D2C4AD09A00A3770F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E269C2C4AD09A00A3770F /* ContentView.swift */; }; 19 | D07E269F2C4AD09A00A3770F /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E269E2C4AD09A00A3770F /* AppModel.swift */; }; 20 | D07E26A12C4AD09A00A3770F /* ToggleImmersiveSpaceButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E26A02C4AD09A00A3770F /* ToggleImmersiveSpaceButton.swift */; }; 21 | D07E26A32C4AD09A00A3770F /* ImmersiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E26A22C4AD09A00A3770F /* ImmersiveView.swift */; }; 22 | D07E26A52C4AD09B00A3770F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07E26A42C4AD09B00A3770F /* Assets.xcassets */; }; 23 | D07E26A82C4AD09B00A3770F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07E26A72C4AD09B00A3770F /* Preview Assets.xcassets */; }; 24 | D07E26B72C4AD1C200A3770F /* SceneMeshProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E26B62C4AD1C200A3770F /* SceneMeshProvider.swift */; }; 25 | D07E26BA2C4AD1CB00A3770F /* SceneMeshAssembler.metal in Sources */ = {isa = PBXBuildFile; fileRef = D07E26B82C4AD1CB00A3770F /* SceneMeshAssembler.metal */; }; 26 | D07E26BB2C4AD1CB00A3770F /* SceneMeshAssembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E26B92C4AD1CB00A3770F /* SceneMeshAssembler.swift */; }; 27 | D0831D862C4B749C0047DABA /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0831D852C4B74980047DABA /* ViewModel.swift */; }; 28 | D0B05E222C650BE000B0D5A3 /* MetalTools in Frameworks */ = {isa = PBXBuildFile; productRef = D00F9AA12C4AD51600E5DD8D /* MetalTools */; }; 29 | D0B05E232C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D033232B2C4B04540051640A /* SimulationTools */; }; 30 | D0B05E242C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D03323312C4B05DE0051640A /* SimulationTools */; }; 31 | D0B05E252C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D0AB7A302C4C9B5F00A02C5A /* SimulationTools */; }; 32 | D0B05E262C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D0795EFE2C4EF061003C3EB3 /* SimulationTools */; }; 33 | D0B05E272C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D027028E2C53042F0024F8D3 /* SimulationTools */; }; 34 | D0B05E282C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D07A0D842C54256B00FEA45C /* SimulationTools */; }; 35 | D0B05E292C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D05E23422C62C8EF0065F0CF /* SimulationTools */; }; 36 | D0B05E2A2C650BE000B0D5A3 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D0C6EFEB2C647FAD0070BA8A /* SimulationTools */; }; 37 | D0D3BD052C650DA900038002 /* SimulationTools in Frameworks */ = {isa = PBXBuildFile; productRef = D0D3BD042C650DA900038002 /* SimulationTools */; }; 38 | D0E84AB62C6164BB00A3B7B1 /* SimulationSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E84AB52C6164BB00A3B7B1 /* SimulationSystem.swift */; }; 39 | D0E84AB82C6164D900A3B7B1 /* ParticlesUpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E84AB72C6164D900A3B7B1 /* ParticlesUpdateSystem.swift */; }; 40 | D0E84ABA2C6164E200A3B7B1 /* SceneUpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E84AB92C6164E200A3B7B1 /* SceneUpdateSystem.swift */; }; 41 | D0E84ABC2C62764E00A3B7B1 /* SettingsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E84ABB2C62764E00A3B7B1 /* SettingsComponent.swift */; }; 42 | D0E84ABE2C6285F000A3B7B1 /* SceneMeshBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E84ABD2C6285E900A3B7B1 /* SceneMeshBuilder.swift */; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | D00F9AC02C4AD80F00E5DD8D /* SceneCollisionEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneCollisionEncoder.swift; sourceTree = ""; }; 47 | D00F9AC22C4AD81500E5DD8D /* SceneCollision.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SceneCollision.metal; sourceTree = ""; }; 48 | D00F9AC82C4AE15100E5DD8D /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; 49 | D0326DFB2C4AE73A008879DD /* ParticleMeshBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticleMeshBuilder.swift; sourceTree = ""; }; 50 | D0326DFD2C4AE789008879DD /* ParticleMeshUpdater.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = ParticleMeshUpdater.metal; sourceTree = ""; }; 51 | D03323262C4B04170051640A /* Simulation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Simulation.swift; sourceTree = ""; }; 52 | D03323282C4B04250051640A /* Simulation.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Simulation.metal; sourceTree = ""; }; 53 | D07E26932C4AD09900A3770F /* Photons.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Photons.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | D07E26972C4AD09A00A3770F /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = ""; }; 55 | D07E269A2C4AD09A00A3770F /* PhotonsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotonsApp.swift; sourceTree = ""; }; 56 | D07E269C2C4AD09A00A3770F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 57 | D07E269E2C4AD09A00A3770F /* AppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModel.swift; sourceTree = ""; }; 58 | D07E26A02C4AD09A00A3770F /* ToggleImmersiveSpaceButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleImmersiveSpaceButton.swift; sourceTree = ""; }; 59 | D07E26A22C4AD09A00A3770F /* ImmersiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmersiveView.swift; sourceTree = ""; }; 60 | D07E26A42C4AD09B00A3770F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 61 | D07E26A72C4AD09B00A3770F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 62 | D07E26A92C4AD09B00A3770F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | D07E26B62C4AD1C200A3770F /* SceneMeshProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneMeshProvider.swift; sourceTree = ""; }; 64 | D07E26B82C4AD1CB00A3770F /* SceneMeshAssembler.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SceneMeshAssembler.metal; sourceTree = ""; }; 65 | D07E26B92C4AD1CB00A3770F /* SceneMeshAssembler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneMeshAssembler.swift; sourceTree = ""; }; 66 | D0831D852C4B74980047DABA /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 67 | D097B6082C6507950036B5CB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 68 | D0E84AB52C6164BB00A3B7B1 /* SimulationSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulationSystem.swift; sourceTree = ""; }; 69 | D0E84AB72C6164D900A3B7B1 /* ParticlesUpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticlesUpdateSystem.swift; sourceTree = ""; }; 70 | D0E84AB92C6164E200A3B7B1 /* SceneUpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneUpdateSystem.swift; sourceTree = ""; }; 71 | D0E84ABB2C62764E00A3B7B1 /* SettingsComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsComponent.swift; sourceTree = ""; }; 72 | D0E84ABD2C6285E900A3B7B1 /* SceneMeshBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneMeshBuilder.swift; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | D07E26902C4AD09900A3770F /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | D0B05E2A2C650BE000B0D5A3 /* SimulationTools in Frameworks */, 81 | D0B05E292C650BE000B0D5A3 /* SimulationTools in Frameworks */, 82 | D0B05E282C650BE000B0D5A3 /* SimulationTools in Frameworks */, 83 | D0B05E272C650BE000B0D5A3 /* SimulationTools in Frameworks */, 84 | D0D3BD052C650DA900038002 /* SimulationTools in Frameworks */, 85 | D0B05E262C650BE000B0D5A3 /* SimulationTools in Frameworks */, 86 | D0B05E252C650BE000B0D5A3 /* SimulationTools in Frameworks */, 87 | D0B05E242C650BE000B0D5A3 /* SimulationTools in Frameworks */, 88 | D0B05E232C650BE000B0D5A3 /* SimulationTools in Frameworks */, 89 | D0B05E222C650BE000B0D5A3 /* MetalTools in Frameworks */, 90 | D046870B2C62C92800BB6557 /* RealityKitContent in Frameworks */, 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 54F137842C5AF08500302974 /* Frameworks */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | ); 101 | name = Frameworks; 102 | sourceTree = ""; 103 | }; 104 | D07E268A2C4AD09900A3770F = { 105 | isa = PBXGroup; 106 | children = ( 107 | D07E26952C4AD09900A3770F /* Photons */, 108 | D07E26962C4AD09A00A3770F /* Packages */, 109 | D07E26942C4AD09900A3770F /* Products */, 110 | 54F137842C5AF08500302974 /* Frameworks */, 111 | D097B6082C6507950036B5CB /* README.md */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | D07E26942C4AD09900A3770F /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | D07E26932C4AD09900A3770F /* Photons.app */, 119 | ); 120 | name = Products; 121 | sourceTree = ""; 122 | }; 123 | D07E26952C4AD09900A3770F /* Photons */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | D0E84AB42C6164A300A3B7B1 /* ECS */, 127 | D0A7A6002C50254500F1F86E /* UI */, 128 | D0A7A5FF2C5024CD00F1F86E /* Scene */, 129 | D0A7A5FE2C5023C300F1F86E /* Particles */, 130 | D0A7A5FD2C5023B300F1F86E /* Simulation */, 131 | D07E269A2C4AD09A00A3770F /* PhotonsApp.swift */, 132 | D00F9AC82C4AE15100E5DD8D /* Utils.h */, 133 | D07E269E2C4AD09A00A3770F /* AppModel.swift */, 134 | D07E26A42C4AD09B00A3770F /* Assets.xcassets */, 135 | D07E26A92C4AD09B00A3770F /* Info.plist */, 136 | D07E26A62C4AD09B00A3770F /* Preview Content */, 137 | ); 138 | path = Photons; 139 | sourceTree = ""; 140 | }; 141 | D07E26962C4AD09A00A3770F /* Packages */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | D07E26972C4AD09A00A3770F /* RealityKitContent */, 145 | ); 146 | path = Packages; 147 | sourceTree = ""; 148 | }; 149 | D07E26A62C4AD09B00A3770F /* Preview Content */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | D07E26A72C4AD09B00A3770F /* Preview Assets.xcassets */, 153 | ); 154 | path = "Preview Content"; 155 | sourceTree = ""; 156 | }; 157 | D0A7A5FD2C5023B300F1F86E /* Simulation */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | D03323262C4B04170051640A /* Simulation.swift */, 161 | D03323282C4B04250051640A /* Simulation.metal */, 162 | ); 163 | path = Simulation; 164 | sourceTree = ""; 165 | }; 166 | D0A7A5FE2C5023C300F1F86E /* Particles */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | D0326DFD2C4AE789008879DD /* ParticleMeshUpdater.metal */, 170 | D0326DFB2C4AE73A008879DD /* ParticleMeshBuilder.swift */, 171 | ); 172 | path = Particles; 173 | sourceTree = ""; 174 | }; 175 | D0A7A5FF2C5024CD00F1F86E /* Scene */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | D00F9AC22C4AD81500E5DD8D /* SceneCollision.metal */, 179 | D00F9AC02C4AD80F00E5DD8D /* SceneCollisionEncoder.swift */, 180 | D07E26B82C4AD1CB00A3770F /* SceneMeshAssembler.metal */, 181 | D07E26B92C4AD1CB00A3770F /* SceneMeshAssembler.swift */, 182 | D0E84ABD2C6285E900A3B7B1 /* SceneMeshBuilder.swift */, 183 | D07E26B62C4AD1C200A3770F /* SceneMeshProvider.swift */, 184 | ); 185 | path = Scene; 186 | sourceTree = ""; 187 | }; 188 | D0A7A6002C50254500F1F86E /* UI */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | D07E26A02C4AD09A00A3770F /* ToggleImmersiveSpaceButton.swift */, 192 | D07E26A22C4AD09A00A3770F /* ImmersiveView.swift */, 193 | D0831D852C4B74980047DABA /* ViewModel.swift */, 194 | D07E269C2C4AD09A00A3770F /* ContentView.swift */, 195 | ); 196 | path = UI; 197 | sourceTree = ""; 198 | }; 199 | D0E84AB42C6164A300A3B7B1 /* ECS */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | D0E84ABB2C62764E00A3B7B1 /* SettingsComponent.swift */, 203 | D0E84AB92C6164E200A3B7B1 /* SceneUpdateSystem.swift */, 204 | D0E84AB72C6164D900A3B7B1 /* ParticlesUpdateSystem.swift */, 205 | D0E84AB52C6164BB00A3B7B1 /* SimulationSystem.swift */, 206 | ); 207 | path = ECS; 208 | sourceTree = ""; 209 | }; 210 | /* End PBXGroup section */ 211 | 212 | /* Begin PBXNativeTarget section */ 213 | D07E26922C4AD09900A3770F /* Photons */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = D07E26AC2C4AD09B00A3770F /* Build configuration list for PBXNativeTarget "Photons" */; 216 | buildPhases = ( 217 | D07E268F2C4AD09900A3770F /* Sources */, 218 | D07E26902C4AD09900A3770F /* Frameworks */, 219 | D07E26912C4AD09900A3770F /* Resources */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | ); 225 | name = Photons; 226 | packageProductDependencies = ( 227 | D07E26982C4AD09A00A3770F /* RealityKitContent */, 228 | D00F9AA12C4AD51600E5DD8D /* MetalTools */, 229 | D033232B2C4B04540051640A /* SimulationTools */, 230 | D03323312C4B05DE0051640A /* SimulationTools */, 231 | D0AB7A302C4C9B5F00A02C5A /* SimulationTools */, 232 | D0795EFE2C4EF061003C3EB3 /* SimulationTools */, 233 | D027028E2C53042F0024F8D3 /* SimulationTools */, 234 | D07A0D842C54256B00FEA45C /* SimulationTools */, 235 | D05E23422C62C8EF0065F0CF /* SimulationTools */, 236 | D0C6EFEB2C647FAD0070BA8A /* SimulationTools */, 237 | D0D3BD042C650DA900038002 /* SimulationTools */, 238 | ); 239 | productName = TheSpatialPhoto; 240 | productReference = D07E26932C4AD09900A3770F /* Photons.app */; 241 | productType = "com.apple.product-type.application"; 242 | }; 243 | /* End PBXNativeTarget section */ 244 | 245 | /* Begin PBXProject section */ 246 | D07E268B2C4AD09900A3770F /* Project object */ = { 247 | isa = PBXProject; 248 | attributes = { 249 | BuildIndependentTargetsInParallel = 1; 250 | LastSwiftUpdateCheck = 1600; 251 | LastUpgradeCheck = 1600; 252 | TargetAttributes = { 253 | D07E26922C4AD09900A3770F = { 254 | CreatedOnToolsVersion = 16.0; 255 | }; 256 | }; 257 | }; 258 | buildConfigurationList = D07E268E2C4AD09900A3770F /* Build configuration list for PBXProject "Photons" */; 259 | compatibilityVersion = "Xcode 15.0"; 260 | developmentRegion = en; 261 | hasScannedForEncodings = 0; 262 | knownRegions = ( 263 | en, 264 | Base, 265 | ); 266 | mainGroup = D07E268A2C4AD09900A3770F; 267 | packageReferences = ( 268 | D0D3BD032C650DA900038002 /* XCRemoteSwiftPackageReference "simulation-tools" */, 269 | ); 270 | productRefGroup = D07E26942C4AD09900A3770F /* Products */; 271 | projectDirPath = ""; 272 | projectRoot = ""; 273 | targets = ( 274 | D07E26922C4AD09900A3770F /* Photons */, 275 | ); 276 | }; 277 | /* End PBXProject section */ 278 | 279 | /* Begin PBXResourcesBuildPhase section */ 280 | D07E26912C4AD09900A3770F /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | D07E26A82C4AD09B00A3770F /* Preview Assets.xcassets in Resources */, 285 | D07E26A52C4AD09B00A3770F /* Assets.xcassets in Resources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXResourcesBuildPhase section */ 290 | 291 | /* Begin PBXSourcesBuildPhase section */ 292 | D07E268F2C4AD09900A3770F /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | D07E26A12C4AD09A00A3770F /* ToggleImmersiveSpaceButton.swift in Sources */, 297 | D0E84ABE2C6285F000A3B7B1 /* SceneMeshBuilder.swift in Sources */, 298 | D00F9AC32C4AD81500E5DD8D /* SceneCollision.metal in Sources */, 299 | D07E269D2C4AD09A00A3770F /* ContentView.swift in Sources */, 300 | D0326DFE2C4AE789008879DD /* ParticleMeshUpdater.metal in Sources */, 301 | D0E84ABC2C62764E00A3B7B1 /* SettingsComponent.swift in Sources */, 302 | D0E84AB62C6164BB00A3B7B1 /* SimulationSystem.swift in Sources */, 303 | D03323292C4B04250051640A /* Simulation.metal in Sources */, 304 | D07E26BA2C4AD1CB00A3770F /* SceneMeshAssembler.metal in Sources */, 305 | D0E84ABA2C6164E200A3B7B1 /* SceneUpdateSystem.swift in Sources */, 306 | D03323272C4B04170051640A /* Simulation.swift in Sources */, 307 | D0326DFC2C4AE73A008879DD /* ParticleMeshBuilder.swift in Sources */, 308 | D0831D862C4B749C0047DABA /* ViewModel.swift in Sources */, 309 | D07E26BB2C4AD1CB00A3770F /* SceneMeshAssembler.swift in Sources */, 310 | D07E26B72C4AD1C200A3770F /* SceneMeshProvider.swift in Sources */, 311 | D07E26A32C4AD09A00A3770F /* ImmersiveView.swift in Sources */, 312 | D07E269F2C4AD09A00A3770F /* AppModel.swift in Sources */, 313 | D07E269B2C4AD09A00A3770F /* PhotonsApp.swift in Sources */, 314 | D0E84AB82C6164D900A3B7B1 /* ParticlesUpdateSystem.swift in Sources */, 315 | D00F9AC12C4AD80F00E5DD8D /* SceneCollisionEncoder.swift in Sources */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | /* End PBXSourcesBuildPhase section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | D07E26AA2C4AD09B00A3770F /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = YES; 332 | CLANG_ENABLE_OBJC_WEAK = YES; 333 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_COMMA = YES; 336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 337 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 338 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 339 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 340 | CLANG_WARN_EMPTY_BODY = YES; 341 | CLANG_WARN_ENUM_CONVERSION = YES; 342 | CLANG_WARN_INFINITE_RECURSION = YES; 343 | CLANG_WARN_INT_CONVERSION = YES; 344 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 346 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 348 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 349 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 350 | CLANG_WARN_STRICT_PROTOTYPES = YES; 351 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 352 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 353 | CLANG_WARN_UNREACHABLE_CODE = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | COPY_PHASE_STRIP = NO; 356 | DEBUG_INFORMATION_FORMAT = dwarf; 357 | ENABLE_STRICT_OBJC_MSGSEND = YES; 358 | ENABLE_TESTABILITY = YES; 359 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 360 | GCC_C_LANGUAGE_STANDARD = gnu17; 361 | GCC_DYNAMIC_NO_PIC = NO; 362 | GCC_NO_COMMON_BLOCKS = YES; 363 | GCC_OPTIMIZATION_LEVEL = 0; 364 | GCC_PREPROCESSOR_DEFINITIONS = ( 365 | "DEBUG=1", 366 | "$(inherited)", 367 | ); 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 375 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 376 | MTL_FAST_MATH = YES; 377 | ONLY_ACTIVE_ARCH = YES; 378 | SDKROOT = xros; 379 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 380 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 381 | XROS_DEPLOYMENT_TARGET = 2.0; 382 | }; 383 | name = Debug; 384 | }; 385 | D07E26AB2C4AD09B00A3770F /* Release */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | ALWAYS_SEARCH_USER_PATHS = NO; 389 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 390 | CLANG_ANALYZER_NONNULL = YES; 391 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 393 | CLANG_ENABLE_MODULES = YES; 394 | CLANG_ENABLE_OBJC_ARC = YES; 395 | CLANG_ENABLE_OBJC_WEAK = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 403 | CLANG_WARN_EMPTY_BODY = YES; 404 | CLANG_WARN_ENUM_CONVERSION = YES; 405 | CLANG_WARN_INFINITE_RECURSION = YES; 406 | CLANG_WARN_INT_CONVERSION = YES; 407 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 408 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 411 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 416 | CLANG_WARN_UNREACHABLE_CODE = YES; 417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 418 | COPY_PHASE_STRIP = NO; 419 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 420 | ENABLE_NS_ASSERTIONS = NO; 421 | ENABLE_STRICT_OBJC_MSGSEND = YES; 422 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 423 | GCC_C_LANGUAGE_STANDARD = gnu17; 424 | GCC_NO_COMMON_BLOCKS = YES; 425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 427 | GCC_WARN_UNDECLARED_SELECTOR = YES; 428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 429 | GCC_WARN_UNUSED_FUNCTION = YES; 430 | GCC_WARN_UNUSED_VARIABLE = YES; 431 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 432 | MTL_ENABLE_DEBUG_INFO = NO; 433 | MTL_FAST_MATH = YES; 434 | SDKROOT = xros; 435 | SWIFT_COMPILATION_MODE = wholemodule; 436 | VALIDATE_PRODUCT = YES; 437 | XROS_DEPLOYMENT_TARGET = 2.0; 438 | }; 439 | name = Release; 440 | }; 441 | D07E26AD2C4AD09B00A3770F /* Debug */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 445 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 446 | CODE_SIGN_IDENTITY = "Apple Development"; 447 | CODE_SIGN_STYLE = Automatic; 448 | CURRENT_PROJECT_VERSION = 1; 449 | DEVELOPMENT_ASSET_PATHS = "\"Photons/Preview Content\""; 450 | DEVELOPMENT_TEAM = 7LT752HSAG; 451 | ENABLE_PREVIEWS = YES; 452 | GENERATE_INFOPLIST_FILE = YES; 453 | INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; 454 | INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "For fun"; 455 | LD_RUNPATH_SEARCH_PATHS = ( 456 | "$(inherited)", 457 | "@executable_path/Frameworks", 458 | ); 459 | MACOSX_DEPLOYMENT_TARGET = 11.0; 460 | MARKETING_VERSION = 1.0; 461 | PRODUCT_BUNDLE_IDENTIFIER = "com.computer-graphics-tools.photons"; 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | PROVISIONING_PROFILE_SPECIFIER = ""; 464 | REGISTER_APP_GROUPS = YES; 465 | SUPPORTED_PLATFORMS = "xros xrsimulator"; 466 | SUPPORTS_MACCATALYST = NO; 467 | SWIFT_EMIT_LOC_STRINGS = YES; 468 | SWIFT_VERSION = 5.0; 469 | TARGETED_DEVICE_FAMILY = 7; 470 | XROS_DEPLOYMENT_TARGET = 2.0; 471 | }; 472 | name = Debug; 473 | }; 474 | D07E26AE2C4AD09B00A3770F /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 478 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 479 | CODE_SIGN_IDENTITY = "Apple Development"; 480 | CODE_SIGN_STYLE = Automatic; 481 | CURRENT_PROJECT_VERSION = 1; 482 | DEVELOPMENT_ASSET_PATHS = "\"Photons/Preview Content\""; 483 | DEVELOPMENT_TEAM = 7LT752HSAG; 484 | ENABLE_PREVIEWS = YES; 485 | GENERATE_INFOPLIST_FILE = YES; 486 | INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; 487 | INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "For fun"; 488 | LD_RUNPATH_SEARCH_PATHS = ( 489 | "$(inherited)", 490 | "@executable_path/Frameworks", 491 | ); 492 | MACOSX_DEPLOYMENT_TARGET = 11.0; 493 | MARKETING_VERSION = 1.0; 494 | PRODUCT_BUNDLE_IDENTIFIER = "com.computer-graphics-tools.photons"; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | PROVISIONING_PROFILE_SPECIFIER = ""; 497 | REGISTER_APP_GROUPS = YES; 498 | SUPPORTED_PLATFORMS = "xros xrsimulator"; 499 | SUPPORTS_MACCATALYST = NO; 500 | SWIFT_EMIT_LOC_STRINGS = YES; 501 | SWIFT_VERSION = 5.0; 502 | TARGETED_DEVICE_FAMILY = 7; 503 | XROS_DEPLOYMENT_TARGET = 2.0; 504 | }; 505 | name = Release; 506 | }; 507 | /* End XCBuildConfiguration section */ 508 | 509 | /* Begin XCConfigurationList section */ 510 | D07E268E2C4AD09900A3770F /* Build configuration list for PBXProject "Photons" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | D07E26AA2C4AD09B00A3770F /* Debug */, 514 | D07E26AB2C4AD09B00A3770F /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | D07E26AC2C4AD09B00A3770F /* Build configuration list for PBXNativeTarget "Photons" */ = { 520 | isa = XCConfigurationList; 521 | buildConfigurations = ( 522 | D07E26AD2C4AD09B00A3770F /* Debug */, 523 | D07E26AE2C4AD09B00A3770F /* Release */, 524 | ); 525 | defaultConfigurationIsVisible = 0; 526 | defaultConfigurationName = Release; 527 | }; 528 | /* End XCConfigurationList section */ 529 | 530 | /* Begin XCRemoteSwiftPackageReference section */ 531 | D0D3BD032C650DA900038002 /* XCRemoteSwiftPackageReference "simulation-tools" */ = { 532 | isa = XCRemoteSwiftPackageReference; 533 | repositoryURL = "https://github.com/computer-graphics-tools/simulation-tools"; 534 | requirement = { 535 | branch = main; 536 | kind = branch; 537 | }; 538 | }; 539 | /* End XCRemoteSwiftPackageReference section */ 540 | 541 | /* Begin XCSwiftPackageProductDependency section */ 542 | D00F9AA12C4AD51600E5DD8D /* MetalTools */ = { 543 | isa = XCSwiftPackageProductDependency; 544 | productName = MetalTools; 545 | }; 546 | D027028E2C53042F0024F8D3 /* SimulationTools */ = { 547 | isa = XCSwiftPackageProductDependency; 548 | productName = SimulationTools; 549 | }; 550 | D033232B2C4B04540051640A /* SimulationTools */ = { 551 | isa = XCSwiftPackageProductDependency; 552 | productName = SimulationTools; 553 | }; 554 | D03323312C4B05DE0051640A /* SimulationTools */ = { 555 | isa = XCSwiftPackageProductDependency; 556 | productName = SimulationTools; 557 | }; 558 | D05E23422C62C8EF0065F0CF /* SimulationTools */ = { 559 | isa = XCSwiftPackageProductDependency; 560 | productName = SimulationTools; 561 | }; 562 | D0795EFE2C4EF061003C3EB3 /* SimulationTools */ = { 563 | isa = XCSwiftPackageProductDependency; 564 | productName = SimulationTools; 565 | }; 566 | D07A0D842C54256B00FEA45C /* SimulationTools */ = { 567 | isa = XCSwiftPackageProductDependency; 568 | productName = SimulationTools; 569 | }; 570 | D07E26982C4AD09A00A3770F /* RealityKitContent */ = { 571 | isa = XCSwiftPackageProductDependency; 572 | productName = RealityKitContent; 573 | }; 574 | D0AB7A302C4C9B5F00A02C5A /* SimulationTools */ = { 575 | isa = XCSwiftPackageProductDependency; 576 | productName = SimulationTools; 577 | }; 578 | D0C6EFEB2C647FAD0070BA8A /* SimulationTools */ = { 579 | isa = XCSwiftPackageProductDependency; 580 | productName = SimulationTools; 581 | }; 582 | D0D3BD042C650DA900038002 /* SimulationTools */ = { 583 | isa = XCSwiftPackageProductDependency; 584 | package = D0D3BD032C650DA900038002 /* XCRemoteSwiftPackageReference "simulation-tools" */; 585 | productName = SimulationTools; 586 | }; 587 | /* End XCSwiftPackageProductDependency section */ 588 | }; 589 | rootObject = D07E268B2C4AD09900A3770F /* Project object */; 590 | } 591 | --------------------------------------------------------------------------------