diff --git a/.swiftpm/xcode/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist b/.swiftpm/xcode/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist index 9fa906d..c25aa13 100644 --- a/.swiftpm/xcode/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/.swiftpm/xcode/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist @@ -71,10 +71,20 @@ orderHint 3 + RealityCheckClient.xcscheme_^#shared#^_ + + orderHint + 12 + RealityCheckConnect.xcscheme_^#shared#^_ orderHint - 7 + 11 + + RealityCheckConnect_visionOS.xcscheme_^#shared#^_ + + orderHint + 3 RealityCheckTests.xcscheme_^#shared#^_ @@ -84,7 +94,7 @@ RealityDumpClient.xcscheme_^#shared#^_ orderHint - 13 + 1 StreamingClient.xcscheme_^#shared#^_ @@ -99,7 +109,7 @@ reality-check-Package.xcscheme_^#shared#^_ orderHint - 13 + 0 SuppressBuildableAutocreation @@ -124,6 +134,11 @@ primary + RealityCheckConnect_visionOS + + primary + + RealityDumpClient primary diff --git a/App/AppPackage/Package.swift b/App/AppPackage/Package.swift index 7d46abc..34e7125 100644 --- a/App/AppPackage/Package.swift +++ b/App/AppPackage/Package.swift @@ -17,7 +17,7 @@ let package = Package( dependencies: [ .package( url: "https://github.com/pointfreeco/swift-composable-architecture", - branch: "prerelease/1.0" + from: "1.2.0" ), .package(name: "reality-check", path: "../.."), ], diff --git a/App/AppPackage/Sources/AppFeature/ARViewSection.swift b/App/AppPackage/Sources/AppFeature/ARViewSection.swift index c338604..949379a 100644 --- a/App/AppPackage/Sources/AppFeature/ARViewSection.swift +++ b/App/AppPackage/Sources/AppFeature/ARViewSection.swift @@ -47,9 +47,10 @@ public struct ARViewSection: Reducer { return .none case .debugOptions(.binding(_)): - return .task { [state] in - .delegate(.didUpdateDebugOptions(state.debugOptions.options)) - } + return .send(.delegate(.didUpdateDebugOptions(state.debugOptions.options))) +// return .task { [state] in +// .delegate(.didUpdateDebugOptions(state.debugOptions.options)) +// } case .debugOptions(_): return .none @@ -59,9 +60,10 @@ public struct ARViewSection: Reducer { case .toggleSelection: state.isSelected.toggle() - return .task { - .delegate(.didToggleSelectSection) - } + return .send(.delegate(.didToggleSelectSection)) +// return .task { +// .delegate(.didToggleSelectSection) +// } } } } diff --git a/App/AppPackage/Sources/AppFeature/AppCore.swift b/App/AppPackage/Sources/AppFeature/AppCore.swift index 169b7a5..734e6f0 100644 --- a/App/AppPackage/Sources/AppFeature/AppCore.swift +++ b/App/AppPackage/Sources/AppFeature/AppCore.swift @@ -12,7 +12,8 @@ public struct AppCore: Reducer { public struct State: Equatable { public var arViewSection: ARViewSection.State? public var entitiesSection: EntitiesSection.State? - @BindingState public var isDumpAreaCollapsed: Bool + @BindingState public var isConsoleCollapsed: Bool + public var isStreaming: Bool public var multipeerConnection: MultipeerConnection.State public var selectedSection: Section? @BindingState public var viewPortSize: CGSize @@ -20,14 +21,16 @@ public struct AppCore: Reducer { public init( arViewSection: ARViewSection.State? = nil, entitiesSection: EntitiesSection.State? = nil, - isDumpAreaDisplayed: Bool = true, + displayConsole: Bool = true, + isStreaming: Bool = false, multipeerConnection: MultipeerConnection.State = .init(), selectedSection: Section? = nil, viewPortSize: CGSize = .zero ) { self.arViewSection = arViewSection self.entitiesSection = entitiesSection - self.isDumpAreaCollapsed = isDumpAreaDisplayed + self.isConsoleCollapsed = !displayConsole + self.isStreaming = isStreaming self.multipeerConnection = multipeerConnection self.selectedSection = selectedSection self.viewPortSize = viewPortSize @@ -60,14 +63,10 @@ public struct AppCore: Reducer { Reduce { state, action in switch action { case .arViewSection(.delegate(.didToggleSelectSection)): - return .task { [state] in - .selectSection(state.selectedSection == .arView ? nil : .arView) - } + return .send(.selectSection(state.selectedSection == .arView ? nil : .arView)) case .arViewSection(.delegate(.didUpdateDebugOptions(let options))): - return .task { - .multipeerConnection(.sendDebugOptions(options)) - } + return .send(.multipeerConnection(.sendDebugOptions(options))) case .arViewSection(_): return .none @@ -76,21 +75,34 @@ public struct AppCore: Reducer { return .none case .entitiesSection(.delegate(.didToggleSelectSection)): - return .task { [state] in - .selectSection((state.entitiesSection?.selection == nil) ? nil : .entities) - } + return .send(.selectSection((state.entitiesSection?.selection == nil) ? nil : .entities)) + + case .entitiesSection(.delegate(.didSelectEntity(let entityID))): + return .send(.multipeerConnection(.sendSelection(entityID))) case .entitiesSection(_): return .none case .multipeerConnection(.delegate(.receivedVideoFrameData(let videoFrameData))): + state.isStreaming = true streamingClient.prepareForRender(videoFrameData) return .none + case .multipeerConnection(.delegate(.receivedRawData(let rawData))): + return .send(.entitiesSection(.dumpOutput(rawData))) + case .multipeerConnection(.delegate(.receivedDecodedARView(let decodedARView))): state.arViewSection = .init(arView: decodedARView) - state.entitiesSection = .init(decodedARView.scene.anchors) - return .none + if state.entitiesSection == nil { + state.entitiesSection = .init(decodedARView.scene.anchors) + } + return .send(.entitiesSection(.refreshEntities(decodedARView.scene.anchors))) + + case .multipeerConnection(.delegate(.receivedDecodedEntities(let decodedEntities))): + if state.entitiesSection == nil { + state.entitiesSection = .init(decodedEntities) + } + return .send(.entitiesSection(.refreshEntities(decodedEntities))) case .multipeerConnection(_): return .none diff --git a/App/AppPackage/Sources/AppFeature/EntitiesSection.swift b/App/AppPackage/Sources/AppFeature/EntitiesSection.swift index 5f3a767..fc8ec50 100644 --- a/App/AppPackage/Sources/AppFeature/EntitiesSection.swift +++ b/App/AppPackage/Sources/AppFeature/EntitiesSection.swift @@ -12,7 +12,7 @@ public struct EntitiesSection: Reducer { public var selectedEntity: IdentifiableEntity? { guard let selection = selection else { return nil } for rootEntity in identifiedEntities { - if let entity = findEntity(root: rootEntity, targetID: selection) { + if let entity = findIdentifiableEntity(root: rootEntity, targetID: selection) { return entity } } @@ -38,11 +38,12 @@ public struct EntitiesSection: Reducer { case binding(BindingAction) case delegate(DelegateAction) case dumpOutput(String) - case select(entity: IdentifiableEntity?) + case refreshEntities([IdentifiableEntity]) } public enum DelegateAction: Equatable { case didToggleSelectSection + case didSelectEntity(IdentifiableEntity.ID) } public var body: some Reducer { @@ -53,14 +54,11 @@ public struct EntitiesSection: Reducer { case .binding(\.$selection): if let entity = state.selectedEntity { return .merge( - .send(.dumpOutput(String(customDumping: entity))), - .send(.delegate(.didToggleSelectSection)) + .send(.delegate(.didToggleSelectSection)), + .send(.delegate(.didSelectEntity(entity.id))) ) } else { - return .merge( - .send(.dumpOutput("...")), - .send(.delegate(.didToggleSelectSection)) - ) + return .send(.delegate(.didToggleSelectSection)) } case .binding: @@ -73,9 +71,11 @@ public struct EntitiesSection: Reducer { state.dumpOutput = output return .none - case .select(let entity): - state.selection = entity?.id - return .none + case .refreshEntities(let entities): + state.identifiedEntities = .init(uniqueElements: entities) + guard let previousSelection = state.selection else { return .none } + state.selection = nil + return .send(.binding(.set(\.$selection, previousSelection))) } } } diff --git a/App/AppPackage/Sources/AppFeature/MultipeerConnection.swift b/App/AppPackage/Sources/AppFeature/MultipeerConnection.swift index 7dcca13..b1e55ed 100644 --- a/App/AppPackage/Sources/AppFeature/MultipeerConnection.swift +++ b/App/AppPackage/Sources/AppFeature/MultipeerConnection.swift @@ -2,9 +2,12 @@ import ComposableArchitecture import Foundation import Models import MultipeerClient +import OSLog import StreamingClient public struct MultipeerConnection: Reducer { + let logger = Logger(subsystem: "AppCore", category: "MultipeerConnection") + public struct ConnectedPeer: Equatable { public let peer: Peer public let discoveryInfo: DiscoveryInfo? @@ -28,6 +31,7 @@ public struct MultipeerConnection: Reducer { case delegate(DelegateAction) case invite(Peer) case sendDebugOptions(_DebugOptions) + case sendSelection(IdentifiableEntity.ID) case start case updatePeers([Peer: DiscoveryInfo]) case updateSessionState(MultipeerClient.SessionState) @@ -36,9 +40,12 @@ public struct MultipeerConnection: Reducer { public enum DelegateAction: Equatable { case didUpdateSessionState(MultipeerClient.SessionState) case receivedDecodedARView(CodableARView) + case receivedDecodedEntities([IdentifiableEntity]) case receivedVideoFrameData(VideoFrameData) + case receivedRawData(String) } + @Dependency(\.continuousClock) var clock @Dependency(\.multipeerClient) var multipeerClient public var body: some Reducer { @@ -60,7 +67,18 @@ public struct MultipeerConnection: Reducer { } return .none + case .sendSelection(let entityID): + do { + let entitySelection = EntitySelection(entityID) + let data = try JSONEncoder().encode(entitySelection) + multipeerClient.send(data) + } catch { + fatalError("Failed to encode selection while sending them.") + } + return .none + case .start: + guard state.connectedPeer == nil else { return .none } return .run(priority: .userInitiated) { send in for await action in await multipeerClient.start( serviceName: "reality-check", @@ -103,9 +121,7 @@ public struct MultipeerConnection: Reducer { case .connected(let peer): state.connectedPeer = .init(peer: peer, discoveryInfo: state.peers[peer]) } - return .task { - .delegate(.didUpdateSessionState(sessionState)) - } + return .send(.delegate(.didUpdateSessionState(sessionState))) } } } @@ -113,24 +129,46 @@ public struct MultipeerConnection: Reducer { extension MultipeerConnection { fileprivate func decodeReceivedData(_ data: Data, send: Send) async { - let decoder = JSONDecoder() - decoder.nonConformingFloatDecodingStrategy = .convertFromString( - positiveInfinity: "INF", - negativeInfinity: "-INF", - nan: "NAN" - ) - //MARK: VideoFrameData - if let videoFrameData = try? decoder.decode(VideoFrameData.self, from: data) { + if let videoFrameData = try? defaultDecoder.decode(VideoFrameData.self, from: data) { await send(.delegate(.receivedVideoFrameData(videoFrameData))) - - } //MARK: CodableARView - else if let decodedARView = try? decoder.decode( + } + // MARK: Raw data + else if let rawData = try? defaultDecoder.decode( + String.self, + from: data + ) { + await send(.delegate(.receivedRawData(rawData))) + } + // MARK: CodableARView + else if let decodedARView = try? defaultDecoder.decode( CodableARView.self, from: data ) { + // FIXME: avoid logger truncating + // print(String(data: data, encoding: .utf8)!) + // logger.debug("\(String(data: data, encoding: .utf8)!, privacy: .public)") await send(.delegate(.receivedDecodedARView(decodedARView))) - } else { + } + // MARK: RealityViewContent Scene + else if let decodedRealityViewContent = try? defaultDecoder.decode( + CodableScene.self, + from: data + ) { + await send(.delegate(.receivedDecodedEntities(decodedRealityViewContent.anchors))) + } + // MARK: RealityViewContent Root + else if let decodedRealityViewContent = try? defaultDecoder.decode( + IdentifiableEntity.self, + from: data + ) { + // FIXME: avoid logger truncating + // print(String(data: data, encoding: .utf8)!) + // logger.debug("\(String(data: data, encoding: .utf8)!, privacy: .public)") + await send(.delegate(.receivedDecodedEntities([decodedRealityViewContent]))) + } + // MARK: default + else { fatalError(String(data: data, encoding: .utf8)!) } } diff --git a/App/AppPackage/Tests/AppFeatureTests/AppFeatureTests.swift b/App/AppPackage/Tests/AppFeatureTests/AppFeatureTests.swift index 48198a7..b70dbe3 100644 --- a/App/AppPackage/Tests/AppFeatureTests/AppFeatureTests.swift +++ b/App/AppPackage/Tests/AppFeatureTests/AppFeatureTests.swift @@ -5,34 +5,13 @@ import XCTest @testable import RealityCheck final class RealityCheckTests: XCTestCase { - var encoder: JSONEncoder { - let encoder = JSONEncoder() - encoder.nonConformingFloatEncodingStrategy = .convertToString( - positiveInfinity: "INF", - negativeInfinity: "-INF", - nan: "NAN" - ) - encoder.outputFormatting = .prettyPrinted - return encoder - } - - var decoder: JSONDecoder { - let decoder = JSONDecoder() - decoder.nonConformingFloatDecodingStrategy = .convertFromString( - positiveInfinity: "INF", - negativeInfinity: "-INF", - nan: "NAN" - ) - return decoder - } - func testSimpleDecodable() throws { let url = Bundle.module.url(forResource: "simple_hierarchy", withExtension: "json")! let data = try Data(contentsOf: url) - let hierarchy = try! decoder.decode(CodableARView.self, from: data) + let hierarchy = try! defaultDecoder.decode(CodableARView.self, from: data) //Re - Encode - let hierarchyData = try! encoder.encode(hierarchy) - let hierarchyReDecoded = try! decoder.decode(CodableARView.self, from: hierarchyData) + let hierarchyData = try! defaultEncoder.encode(hierarchy) + let hierarchyReDecoded = try! defaultDecoder.decode(CodableARView.self, from: hierarchyData) XCTAssertFalse(hierarchyReDecoded.scene.anchors.isEmpty) customDump(hierarchyReDecoded) @@ -49,7 +28,7 @@ final class RealityCheckTests: XCTestCase { func testARViewDecodable() throws { let url = Bundle.module.url(forResource: "codable_arview", withExtension: "json")! let data = try Data(contentsOf: url, options: .mappedIfSafe) - let hierarchy = try! decoder.decode(CodableARView.self, from: data) + let hierarchy = try! defaultDecoder.decode(CodableARView.self, from: data) XCTAssertNotNil(hierarchy) customDump(hierarchy) } diff --git a/App/RealityCheck.xcodeproj/project.pbxproj b/App/RealityCheck.xcodeproj/project.pbxproj index b758241..b1b5ce0 100644 --- a/App/RealityCheck.xcodeproj/project.pbxproj +++ b/App/RealityCheck.xcodeproj/project.pbxproj @@ -19,10 +19,10 @@ 401215B62A237A2000D98058 /* EntitiesSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401215B52A237A2000D98058 /* EntitiesSectionView.swift */; }; 401215B82A237E5000D98058 /* ARViewSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401215B72A237E5000D98058 /* ARViewSectionView.swift */; }; 4025FC752A0BF68100A8191F /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4025FC742A0BF68100A8191F /* SidebarView.swift */; }; - 402EA02E29F6E43E00BB522F /* EntityDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EA02D29F6E43E00BB522F /* EntityDetailView.swift */; }; + 402EA02E29F6E43E00BB522F /* EntityInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EA02D29F6E43E00BB522F /* EntityInspectorView.swift */; }; 4041812A2A1BE1EF003C198E /* ComponentPropertiesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404181292A1BE1EF003C198E /* ComponentPropertiesView.swift */; }; 4076368B2A1EBB23008378B6 /* Color+Hash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4076368A2A1EBB23008378B6 /* Color+Hash.swift */; }; - 40874E8F2A289B9D00DD88FD /* ARViewDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40874E8E2A289B9D00DD88FD /* ARViewDetailView.swift */; }; + 40874E8F2A289B9D00DD88FD /* ARViewInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40874E8E2A289B9D00DD88FD /* ARViewInspectorView.swift */; }; 40876A542A1E0F4A00C96893 /* ConnectionSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40876A532A1E0F4A00C96893 /* ConnectionSetupView.swift */; }; 40E7518B29F68F0B0083502A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40E7517D29F68F0B0083502A /* Assets.xcassets */; }; 40E7518C29F68F0B0083502A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40E7517F29F68F0B0083502A /* Preview Assets.xcassets */; }; @@ -48,14 +48,13 @@ 401215B52A237A2000D98058 /* EntitiesSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntitiesSectionView.swift; sourceTree = ""; }; 401215B72A237E5000D98058 /* ARViewSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ARViewSectionView.swift; sourceTree = ""; }; 4025FC742A0BF68100A8191F /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; - 402EA02D29F6E43E00BB522F /* EntityDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityDetailView.swift; sourceTree = ""; }; + 402EA02D29F6E43E00BB522F /* EntityInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityInspectorView.swift; sourceTree = ""; }; 404181292A1BE1EF003C198E /* ComponentPropertiesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComponentPropertiesView.swift; sourceTree = ""; }; 4076368A2A1EBB23008378B6 /* Color+Hash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Hash.swift"; sourceTree = ""; }; 4086EDAC2A16C326001CFF27 /* AppPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = AppPackage; sourceTree = ""; }; - 40874E8E2A289B9D00DD88FD /* ARViewDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ARViewDetailView.swift; sourceTree = ""; }; + 40874E8E2A289B9D00DD88FD /* ARViewInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ARViewInspectorView.swift; sourceTree = ""; }; 40876A532A1E0F4A00C96893 /* ConnectionSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionSetupView.swift; sourceTree = ""; }; 408A519C29F68FD6001E7D9B /* RealityCheck.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RealityCheck.entitlements; sourceTree = ""; }; - 40BD84CE2A16808400522CED /* RealityCheck-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RealityCheck-Info.plist"; sourceTree = ""; }; 40E7517D29F68F0B0083502A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40E7517F29F68F0B0083502A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 40E7518129F68F0B0083502A /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; @@ -103,6 +102,15 @@ path = Sidebar; sourceTree = ""; }; + 402D8A9F2A7E3DE8004DC8F5 /* Extensions */ = { + isa = PBXGroup; + children = ( + 4076368A2A1EBB23008378B6 /* Color+Hash.swift */, + 401215B02A23659D00D98058 /* View+isHovering.swift */, + ); + path = Extensions; + sourceTree = ""; + }; 409B76522A16D2D1001B28E1 /* Packages */ = { isa = PBXGroup; children = ( @@ -116,20 +124,18 @@ isa = PBXGroup; children = ( 408A519C29F68FD6001E7D9B /* RealityCheck.entitlements */, - 40BD84CE2A16808400522CED /* RealityCheck-Info.plist */, - 40874E8E2A289B9D00DD88FD /* ARViewDetailView.swift */, - 4076368A2A1EBB23008378B6 /* Color+Hash.swift */, + 40874E8E2A289B9D00DD88FD /* ARViewInspectorView.swift */, 404181292A1BE1EF003C198E /* ComponentPropertiesView.swift */, 40876A532A1E0F4A00C96893 /* ConnectionSetupView.swift */, 401215A12A210AC100D98058 /* DebugOptionsView.swift */, - 402EA02D29F6E43E00BB522F /* EntityDetailView.swift */, + 402EA02D29F6E43E00BB522F /* EntityInspectorView.swift */, 40E7518129F68F0B0083502A /* MainView.swift */, 40E7518229F68F0B0083502A /* RealityCheckApp.swift */, 401215B12A23659D00D98058 /* StatusBarView.swift */, - 401215B02A23659D00D98058 /* View+isHovering.swift */, 40E7517D29F68F0B0083502A /* Assets.xcassets */, 401215A32A23658A00D98058 /* CustomSplitView */, 40E7518329F68F0B0083502A /* Debug */, + 402D8A9F2A7E3DE8004DC8F5 /* Extensions */, 40E7517E29F68F0B0083502A /* Preview Content */, 401215B42A2379F200D98058 /* Sidebar */, ); @@ -271,9 +277,9 @@ 40876A542A1E0F4A00C96893 /* ConnectionSetupView.swift in Sources */, 401215AE2A23658A00D98058 /* Variadic.swift in Sources */, 401215B22A23659D00D98058 /* View+isHovering.swift in Sources */, - 402EA02E29F6E43E00BB522F /* EntityDetailView.swift in Sources */, + 402EA02E29F6E43E00BB522F /* EntityInspectorView.swift in Sources */, 401215AC2A23658A00D98058 /* SplitViewControllerView.swift in Sources */, - 40874E8F2A289B9D00DD88FD /* ARViewDetailView.swift in Sources */, + 40874E8F2A289B9D00DD88FD /* ARViewInspectorView.swift in Sources */, 40E7519129F68F0B0083502A /* Transform+Rotation.swift in Sources */, 40E7519229F68F0B0083502A /* Comparable+clamp.swift in Sources */, ); @@ -410,7 +416,6 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "macOS/RealityCheck-Info.plist"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSLocalNetworkUsageDescription = "This application will use local networking to discover nearby devices."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; @@ -431,10 +436,10 @@ PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.RealityCheck; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = macosx; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -453,7 +458,6 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "macOS/RealityCheck-Info.plist"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSLocalNetworkUsageDescription = "This application will use local networking to discover nearby devices."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; @@ -474,10 +478,10 @@ PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.RealityCheck; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = macosx; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/App/RealityCheck.xcodeproj/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist b/App/RealityCheck.xcodeproj/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist index ea171a6..4229bfa 100644 --- a/App/RealityCheck.xcodeproj/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/App/RealityCheck.xcodeproj/xcuserdata/cristian.xcuserdatad/xcschemes/xcschememanagement.plist @@ -91,7 +91,7 @@ RealityCheck.xcscheme_^#shared#^_ orderHint - 0 + 2 SuppressBuildableAutocreation diff --git a/App/macOS/ARViewDetailView.swift b/App/macOS/ARViewInspectorView.swift similarity index 90% rename from App/macOS/ARViewDetailView.swift rename to App/macOS/ARViewInspectorView.swift index 259607e..3b19113 100644 --- a/App/macOS/ARViewDetailView.swift +++ b/App/macOS/ARViewInspectorView.swift @@ -1,7 +1,7 @@ import Models import SwiftUI -struct ARViewDetailView: View { +struct ARViewInspectorView: View { let arView: CodableARView init( diff --git a/App/macOS/Assets.xcassets/Badge - ARKit.imageset/Contents.json b/App/macOS/Assets.xcassets/Badge - ARKit.imageset/Contents.json index 37c9122..53e7dfa 100644 --- a/App/macOS/Assets.xcassets/Badge - ARKit.imageset/Contents.json +++ b/App/macOS/Assets.xcassets/Badge - ARKit.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "Badge - ARKit.pdf", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/App/macOS/Assets.xcassets/stripes.imageset/Contents.json b/App/macOS/Assets.xcassets/stripes.imageset/Contents.json new file mode 100644 index 0000000..9512045 --- /dev/null +++ b/App/macOS/Assets.xcassets/stripes.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "stripes-light.png", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "stripes-dark.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/App/macOS/Assets.xcassets/stripes.imageset/stripes-dark.png b/App/macOS/Assets.xcassets/stripes.imageset/stripes-dark.png new file mode 100644 index 0000000..2e23964 Binary files /dev/null and b/App/macOS/Assets.xcassets/stripes.imageset/stripes-dark.png differ diff --git a/App/macOS/Assets.xcassets/stripes.imageset/stripes-light.png b/App/macOS/Assets.xcassets/stripes.imageset/stripes-light.png new file mode 100644 index 0000000..4ec6594 Binary files /dev/null and b/App/macOS/Assets.xcassets/stripes.imageset/stripes-light.png differ diff --git a/App/macOS/Assets.xcassets/visionpro.imageset/Contents.json b/App/macOS/Assets.xcassets/visionpro.imageset/Contents.json new file mode 100644 index 0000000..f669181 --- /dev/null +++ b/App/macOS/Assets.xcassets/visionpro.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "visionos-icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/App/macOS/Assets.xcassets/visionpro.imageset/visionos-icon.svg b/App/macOS/Assets.xcassets/visionpro.imageset/visionos-icon.svg new file mode 100644 index 0000000..e67c7d2 --- /dev/null +++ b/App/macOS/Assets.xcassets/visionpro.imageset/visionos-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/App/macOS/ConnectionSetupView.swift b/App/macOS/ConnectionSetupView.swift index 656639e..1bcd0da 100644 --- a/App/macOS/ConnectionSetupView.swift +++ b/App/macOS/ConnectionSetupView.swift @@ -9,9 +9,8 @@ struct ConnectionSetupView: View { let store: StoreOf var columns: [GridItem] { [ - .init(.flexible()), - .init(.flexible()), - .init(.flexible()), + .init(.flexible(), spacing: 16), + .init(.flexible(), spacing: 16), ] } @@ -20,7 +19,7 @@ struct ConnectionSetupView: View { WithViewStore(self.store, observe: { $0 }) { viewStore in VStack(spacing: 0) { ScrollView(.vertical) { - LazyVGrid(columns: columns, alignment: .leading) { + LazyVGrid(columns: columns) { ForEach(Array(viewStore.peers.keys)) { peer in PeerConnectView(peer: peer, viewStore: viewStore) } @@ -30,7 +29,7 @@ struct ConnectionSetupView: View { .overlay( Group { if viewStore.peers.isEmpty { - Text("Inspectable apps will appear here.") + Text("Inspectable apps will appear here") .foregroundColor(.secondary) } } @@ -63,7 +62,7 @@ struct ConnectionSetupView: View { .padding() .background(.bar) } - .frame(width: 521, height: 521) + .frame(width: 521 / 1.25, height: 521 / 1.25) } } } @@ -72,6 +71,14 @@ struct PeerConnectView: View { @Environment(\.openWindow) private var openWindow let peer: Peer @ObservedObject var viewStore: ViewStoreOf + var appIconName: String { + guard let device = viewStore.peers[peer]?.device else { return "app" } + if device.lowercased().contains("vision") { + return "circle.fill" + } else { + return "app.fill" + } + } var body: some View { let discoveryInfo: DiscoveryInfo? = viewStore.peers[peer] @@ -82,7 +89,7 @@ struct PeerConnectView: View { }, label: { VStack { - Image(systemName: "app.fill") + Image(systemName: appIconName) .resizable() .aspectRatio(contentMode: .fill) .fontWeight(.thin) @@ -101,7 +108,19 @@ struct PeerConnectView: View { GroupBox { if let device = discoveryInfo?.device { - Label(device, systemImage: "iphone") + Label { + Text(device) + } icon: { + if device.lowercased().contains("vision") { + if #available(visionOS 1.0, *) { + Image(systemName: "visionpro") + } else { + Image("visionpro") + } + } else { + Image(systemName: "iphone") + } + } } if let system = discoveryInfo?.system { @@ -112,19 +131,19 @@ struct PeerConnectView: View { } .padding() .background( - RoundedRectangle(cornerRadius: 4, style: .continuous) + RoundedRectangle(cornerRadius: 12, style: .continuous) .fill(Color(nsColor: .controlBackgroundColor)) ) .drawingGroup() .overlay( - RoundedRectangle(cornerRadius: 4, style: .continuous) + RoundedRectangle(cornerRadius: 12, style: .continuous) .stroke(lineWidth: 0.2) .foregroundColor(.secondary) ) } ) .buttonStyle(.plain) - .shadow(radius: 8, y: 8) + .shadow(radius: 4, y: 2) .help("Insert coin to continue") } } @@ -136,7 +155,7 @@ struct ConnectionSetupView_Previews: PreviewProvider { initialState: AppCore.State( multipeerConnection: .init() ), - reducer: AppCore() + reducer: AppCore.init ) .scope( state: \.multipeerConnection, @@ -158,7 +177,7 @@ struct ConnectionSetupView_Previews: PreviewProvider { ] ) ), - reducer: AppCore() + reducer: AppCore.init ) .scope( state: \.multipeerConnection, @@ -204,7 +223,7 @@ struct ConnectionSetupView_Previews: PreviewProvider { ] ) ), - reducer: AppCore() + reducer: AppCore.init ) .scope( state: \.multipeerConnection, diff --git a/App/macOS/DebugOptionsView.swift b/App/macOS/DebugOptionsView.swift index cd7a4bb..006b384 100644 --- a/App/macOS/DebugOptionsView.swift +++ b/App/macOS/DebugOptionsView.swift @@ -16,18 +16,18 @@ public struct DebugOptionsView: View { Form { Section( content: { - Toggle("Anchor Geometry", isOn: viewStore.binding(\.$showAnchorGeometry)) + Toggle("Anchor Geometry", isOn: viewStore.$showAnchorGeometry) .help("Display anchor geometry.") - Toggle("Anchor Origins", isOn: viewStore.binding(\.$showAnchorOrigins)) + Toggle("Anchor Origins", isOn: viewStore.$showAnchorOrigins) .help("Display anchor origins.") - Toggle("Feature Points", isOn: viewStore.binding(\.$showFeaturePoints)) + Toggle("Feature Points", isOn: viewStore.$showFeaturePoints) .help( "Display a point cloud showing intermediate results of the scene analysis used to track device position." ) - Toggle("Physics", isOn: viewStore.binding(\.$showPhysics)) + Toggle("Physics", isOn: viewStore.$showPhysics) .help("Draw visualizations for collision objects and rigid bodies.") //TODO: find a way to verify this value @@ -35,10 +35,10 @@ public struct DebugOptionsView: View { // .help("Display the depth-colored wireframe for scene understanding meshes.") // .disabled(!ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh)) - Toggle("Statistics", isOn: viewStore.binding(\.$showStatistics)) + Toggle("Statistics", isOn: viewStore.$showStatistics) .help("Collect performance statistics and display them in the view.") - Toggle("World Origin", isOn: viewStore.binding(\.$showWorldOrigin)) + Toggle("World Origin", isOn: viewStore.$showWorldOrigin) .help( "Display a coordinate axis indicating the position and orientation of the AR world coordinate system." ) diff --git a/App/macOS/EntityDetailView.swift b/App/macOS/EntityDetailView.swift deleted file mode 100644 index 8c128b2..0000000 --- a/App/macOS/EntityDetailView.swift +++ /dev/null @@ -1,129 +0,0 @@ -import Models -import SwiftUI - -struct EntityDetailView: View { - let entity: IdentifiableEntity - - init( - _ entity: IdentifiableEntity - ) { - self.entity = entity - } - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - VStack(alignment: .leading) { - Label( - entity.entityType.description, - systemImage: entity.entityType.symbol - ) - .font(.headline) - - Section { - LabeledContent( - "id:", - value: entity.id.description - ) - .textSelection(.enabled) - - if let name = entity.name, !name.isEmpty { - LabeledContent( - "name:", - value: name - ) - .textSelection(.enabled) - } - - if let anchorIdentifier = entity.anchorIdentifier { - LabeledContent( - "anchorIdentifier", - value: anchorIdentifier.uuidString - ) - .textSelection(.enabled) - } - } - } - .padding() - - Divider() - - Form { - Section("Accessibility") { - LabeledContent( - "isAccessibilityElement", - value: - "\(entity.isAccessibilityElement ? "YES" : "NO")" - ) - if let accessibilityLabel = entity.accessibilityLabel { - LabeledContent( - "accessibilityLabel", - value: accessibilityLabel - ) - } - if let accessibilityDescription = entity - .accessibilityDescription - { - LabeledContent( - "accessibilityDescription", - value: accessibilityDescription - ) - } - } - - if !entity.availableAnimations.isEmpty { - Section("Animation") { - DisclosureGroup("availableAnimations") { - Text(String(customDumping: entity.availableAnimations)) - .monospaced() - .textSelection(.enabled) - } - - } - } - - Section("State") { - LabeledContent( - "isEnabled", - value: "\(entity.state.isEnabled ? "YES" : "NO")" - ) - LabeledContent( - "isEnabledInHierarchy", - value: "\(entity.state.isActive ? "YES" : "NO")" - ) - LabeledContent( - "isAnchored", - value: - "\(entity.state.isEnabledInHierarchy ? "YES" : "NO")" - ) - LabeledContent( - "isActive", - value: "\(entity.state.isAnchored ? "YES" : "NO")" - ) - } - - Section("Hierarhy") { - LabeledContent( - "parent", - value: "\(entity.hierarhy.hasParent ? "YES" : "NO")" - ) - LabeledContent( - "children count", - value: "\(entity.hierarhy.childrenCount)" - ) - } - - Section("Components") { - LabeledContent("count", value: "\(entity.components.count)") - ForEach(entity.components.components, id: \.self) { component in - DisclosureGroup(component.componentType.description) { - ComponentPropertiesView(component.properties) - .monospaced() - } - .help(component.componentType.help) - } - } - } - .formStyle(.grouped) - } - } -} diff --git a/App/macOS/EntityInspectorView.swift b/App/macOS/EntityInspectorView.swift new file mode 100644 index 0000000..ba12b5c --- /dev/null +++ b/App/macOS/EntityInspectorView.swift @@ -0,0 +1,153 @@ +import AppFeature +import ComposableArchitecture +import Models +import SwiftUI + +struct EntityInspectorView: View { + let store: StoreOf + let viewStore: ViewStoreOf + + var entity: IdentifiableEntity? { + viewStore.selectedEntity + } + + init( + _ store: StoreOf + ) { + self.store = store + self.viewStore = .init(store, observe: { $0 }) + } + + var body: some View { + if let entity { + VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading) { + Label( + entity.entityType.description, + systemImage: entity.entityType.symbol + ) + .font(.headline) + + Section { + LabeledContent( + "id:", + value: entity.id.description + ) + .textSelection(.enabled) + + if let name = entity.name, !name.isEmpty { + LabeledContent( + "name:", + value: name + ) + } + + if let anchorIdentifier = entity.anchorIdentifier { + LabeledContent( + "anchorIdentifier", + value: anchorIdentifier.uuidString + ) + } + } + } + .padding() + + Divider() + + List { + Section("Accessibility") { + LabeledContent( + "isAccessibilityElement", + value: + "\(entity.isAccessibilityElement ? "YES" : "NO")" + ) + if let accessibilityLabel = entity.accessibilityLabel { + LabeledContent( + "accessibilityLabel", + value: accessibilityLabel + ) + } + if let accessibilityDescription = entity + .accessibilityDescription + { + LabeledContent( + "accessibilityDescription", + value: accessibilityDescription + ) + } + } + + if !entity.availableAnimations.isEmpty { + Section("Animation") { + DisclosureGroup("availableAnimations") { + Text(String(customDumping: entity.availableAnimations)) + .monospaced() + .textSelection(.enabled) + } + } + } + + Section("State") { + LabeledContent( + "isEnabled", + value: "\(entity.state.isEnabled ? "YES" : "NO")" + ) + LabeledContent( + "isEnabledInHierarchy", + value: "\(entity.state.isActive ? "YES" : "NO")" + ) + LabeledContent( + "isAnchored", + value: + "\(entity.state.isEnabledInHierarchy ? "YES" : "NO")" + ) + LabeledContent( + "isActive", + value: "\(entity.state.isAnchored ? "YES" : "NO")" + ) + } + + Section("Hierarhy") { + if let parentID = entity.hierarhy.parentID { + LabeledContent( + "parent", + content: { + Button( + parentID.description, + systemImage: "arrow.up.forward.square.fill", + action: { viewStore.send(.binding(.set(\.$selection, parentID))) } + ) + .help( + """ + Click to select the parent. + ID: \(parentID.description) + """ + ) + .padding(1) + } + ) + } + LabeledContent( + "children count", + value: "\(entity.hierarhy.childrenCount)" + ) + } + + Section("Components") { + LabeledContent("count", value: "\(entity.components.count)") + ForEach(entity.components.components, id: \.self) { component in + GroupBox { + DisclosureGroup(component.componentType.description) { + ComponentPropertiesView(component.properties) + .monospaced() + } + .help(component.componentType.help) + } + } + } + } + } + .textSelection(.enabled) + } + } +} diff --git a/App/macOS/Color+Hash.swift b/App/macOS/Extensions/Color+Hash.swift similarity index 100% rename from App/macOS/Color+Hash.swift rename to App/macOS/Extensions/Color+Hash.swift diff --git a/App/macOS/View+isHovering.swift b/App/macOS/Extensions/View+isHovering.swift similarity index 100% rename from App/macOS/View+isHovering.swift rename to App/macOS/Extensions/View+isHovering.swift diff --git a/App/macOS/MainView.swift b/App/macOS/MainView.swift index 9cd58de..f39e109 100644 --- a/App/macOS/MainView.swift +++ b/App/macOS/MainView.swift @@ -33,39 +33,52 @@ struct MainView: View { SidebarView(store: store) } content: { ZStack { - StreamingView(viewportSize: viewStore.binding(\.$viewPortSize)) - .frame(maxWidth: viewStore.viewPortSize.width, maxHeight: viewStore.viewPortSize.height) - .aspectRatio( - viewStore.viewPortSize.width / viewStore.viewPortSize.height, - contentMode: .fit - ) - .overlay { - Rectangle().stroke() - } - .background(Color(nsColor: .controlBackgroundColor)) - .padding() - .padding(.bottom, 32) + if viewStore.isStreaming { + MetalViewRepresentable(viewportSize: viewStore.$viewPortSize) + .frame( + maxWidth: viewStore.viewPortSize.width, + maxHeight: viewStore.viewPortSize.height + ) + .aspectRatio( + viewStore.viewPortSize.width / viewStore.viewPortSize.height, + contentMode: .fit + ) + .mask(RoundedRectangle(cornerRadius: 64, style: .continuous)) + .overlay { + ///inner corner radius + padding = outer corner radius + RoundedRectangle(cornerRadius: 64, style: .continuous) + .stroke() + .foregroundStyle(.secondary) + } + .padding() + .padding(.bottom, 32) + } else { + PreviewPausedView() + } SplitViewReader { proxy in SplitView(axis: .vertical) { Color.clear .safeAreaInset(edge: .bottom, spacing: 0) { - StatusBarView(proxy: proxy, collapsed: viewStore.binding(\.$isDumpAreaCollapsed)) + StatusBarView(proxy: proxy, collapsed: viewStore.$isConsoleCollapsed) } - TextEditor(text: .constant(viewStore.entitiesSection?.dumpOutput ?? "???")) - .font(.body) - .monospaced() + TextEditor(text: .constant(viewStore.entitiesSection?.dumpOutput ?? "...")) + .font(.system(.body, design: .monospaced)) .collapsable() - .collapsed(viewStore.binding(\.$isDumpAreaCollapsed)) + .collapsed(viewStore.$isConsoleCollapsed) .frame(minHeight: 200, maxHeight: .infinity) } .edgesIgnoringSafeArea(.top) .frame(maxWidth: .infinity, maxHeight: .infinity) } } - .background(Color(nsColor: .lightGray)) - + .background( + Image("stripes") + .resizable(resizingMode: .tile) + .scaleEffect(4) + .opacity(viewStore.isStreaming ? 0 : 1) + ) } detail: { switch viewStore.selectedSection { case .none: @@ -73,15 +86,19 @@ struct MainView: View { case .arView: if let arView = viewStore.arViewSection?.arView { - ARViewDetailView(arView) + ARViewInspectorView(arView) .navigationSplitViewColumnWidth(min: 270, ideal: 405, max: 810) } case .entities: - if let entity = viewStore.entitiesSection?.selectedEntity { - EntityDetailView(entity) - .navigationSplitViewColumnWidth(min: 270, ideal: 405, max: 810) - } + IfLetStore( + store.scope( + state: \.entitiesSection, + action: AppCore.Action.entitiesSection + ), + then: EntityInspectorView.init + ) + .navigationSplitViewColumnWidth(min: 270, ideal: 405, max: 810) } } .navigationSplitViewStyle(.balanced) @@ -159,10 +176,42 @@ struct ContentView_Previews: PreviewProvider { ), entitiesSection: .init([], selection: 14_973_088_022_893_562_172) ), - reducer: AppCore() + reducer: { + AppCore() + .dependency(\.multipeerClient, .testValue) + } ) ) .navigationSplitViewStyle(.prominentDetail) .frame(width: 500, height: 900) } } + +struct PreviewPausedView: View { + var body: some View { + VStack { + HStack { + Text("Screen capture paused") + .font(.headline) + + Spacer().frame(maxWidth: 100) + Button.init( + "Help", + systemImage: "questionmark.circle", + action: {} + ) + .controlSize(.large) + .buttonStyle(.plain) + .labelStyle(.iconOnly) + } + .padding() + .background( + RoundedRectangle(cornerRadius: 16, style: .continuous) + .fill(Color(nsColor: .controlBackgroundColor)) + .shadow(radius: 4) + ) + Spacer() + } + .padding(.top, 32) + } +} diff --git a/App/macOS/RealityCheck.entitlements b/App/macOS/RealityCheck.entitlements index b6f9416..a758fa7 100644 --- a/App/macOS/RealityCheck.entitlements +++ b/App/macOS/RealityCheck.entitlements @@ -4,6 +4,8 @@ com.apple.security.app-sandbox + com.apple.security.cs.debugger + com.apple.security.device.bluetooth com.apple.security.device.usb diff --git a/App/macOS/RealityCheckApp.swift b/App/macOS/RealityCheckApp.swift index fd21d5f..57a9ca0 100644 --- a/App/macOS/RealityCheckApp.swift +++ b/App/macOS/RealityCheckApp.swift @@ -6,8 +6,10 @@ import SwiftUI struct RealityCheckApp: App { let store: StoreOf = .init( initialState: AppCore.State(), - reducer: AppCore() - // .dependency(\.multipeerClient, .testValue) + reducer: { + AppCore() + // .dependency(\.multipeerClient, .testValue) + } ) var body: some Scene { @@ -21,6 +23,7 @@ struct RealityCheckApp: App { } .windowResizability(.contentSize) .defaultPosition(.center) + .windowStyle(.hiddenTitleBar) Window("RealityCheck", id: "RealityCheckWindowID") { MainView(store: store) diff --git a/App/macOS/Sidebar/ARViewSectionView.swift b/App/macOS/Sidebar/ARViewSectionView.swift index b68f404..3c601ff 100644 --- a/App/macOS/Sidebar/ARViewSectionView.swift +++ b/App/macOS/Sidebar/ARViewSectionView.swift @@ -53,7 +53,7 @@ struct ARViewSectionView: View { .padding(.trailing, 4) .help("ARView Debug Options") .popover( - isPresented: viewStore.binding(\.$isDebugOptionsDisplayed), + isPresented: viewStore.$isDebugOptionsDisplayed, arrowEdge: .trailing ) { DebugOptionsView( diff --git a/App/macOS/Sidebar/EntitiesSectionView.swift b/App/macOS/Sidebar/EntitiesSectionView.swift index d5d06a5..c90a3b4 100644 --- a/App/macOS/Sidebar/EntitiesSectionView.swift +++ b/App/macOS/Sidebar/EntitiesSectionView.swift @@ -7,53 +7,44 @@ struct EntitiesSectionView: View { var body: some View { WithViewStore(store, observe: { $0 }) { viewStore in - List(selection: viewStore.binding(\.$selection)) { - Section("Entities") { - OutlineGroup( - viewStore.identifiedEntities.elements, - children: \.children - ) { entity in - HStack { - if let name = entity.name, !name.isEmpty { - Label( - title: { - VStack(alignment: .leading) { - Text(name) - Text(entity.entityType.description) - .font(.caption) - .foregroundColor(.secondary) - } - }, - icon: { - Image(systemName: entity.entityType.symbol) - } - ) - } else { - Label(entity.entityType.description, systemImage: entity.entityType.symbol) + List( + viewStore.identifiedEntities.elements, + children: \.children, + selection: viewStore.$selection + ) { entity in + HStack { + if let name = entity.name, !name.isEmpty { + Label( + title: { + VStack(alignment: .leading) { + Text(name) + Text(entity.entityType.description) + .font(.caption) + .foregroundColor(.secondary) + } + }, + icon: { + Image(systemName: entity.entityType.symbol) } + ) + } else { + Label(entity.entityType.description, systemImage: entity.entityType.symbol) + } - if let children = entity.children { - Spacer() - Text("\(children.count)") - .font(.caption2) - .foregroundColor(.white) - .padding(.vertical, 2) - .padding(.horizontal, 6) - .background(Capsule(style: .continuous).fill(Color(.controlAccentColor))) - } - } - .help(entity.entityType.help) - .accessibilityLabel(Text(entity.accessibilityLabel ?? "")) - .accessibilityValue(Text(entity.accessibilityDescription ?? "")) + if let children = entity.children { + Spacer() + Text("\(children.count)") + .font(.caption2) + .foregroundColor(.white) + .padding(.vertical, 2) + .padding(.horizontal, 6) + .background(Capsule(style: .continuous).fill(Color(.controlAccentColor))) } } + .help(entity.entityType.help) + .accessibilityLabel(Text(entity.accessibilityLabel ?? "")) + .accessibilityValue(Text(entity.accessibilityDescription ?? "")) } } } } - -// struct MyView_Previews: PreviewProvider { -// static var previews: some View { -// EntitiesView(store: ...) -// } -// } diff --git a/App/macOS/Sidebar/SidebarView.swift b/App/macOS/Sidebar/SidebarView.swift index 2eb7597..2e7cb10 100644 --- a/App/macOS/Sidebar/SidebarView.swift +++ b/App/macOS/Sidebar/SidebarView.swift @@ -7,26 +7,26 @@ struct SidebarView: View { let store: StoreOf var body: some View { - VStack(spacing: 0) { - + VStack { //MARK: ARView & Scenes + IfLetStore( self.store.scope( state: \.arViewSection, action: AppCore.Action.arViewSection ), - then: ARViewSectionView.init(store:), - else: { Text("//TODO: ARView options placeholder") } + then: ARViewSectionView.init(store:) ) //MARK: Entities + IfLetStore( self.store.scope( state: \.entitiesSection, action: AppCore.Action.entitiesSection ), then: EntitiesSectionView.init(store:), - else: { Text("//TODO: Entities placeholder") } + else: { Text("No Entities").foregroundStyle(.secondary) } ) } } diff --git a/Examples/Connect/Connect.xcodeproj/project.pbxproj b/Examples/iOS/Connect/Connect.xcodeproj/project.pbxproj similarity index 100% rename from Examples/Connect/Connect.xcodeproj/project.pbxproj rename to Examples/iOS/Connect/Connect.xcodeproj/project.pbxproj diff --git a/Examples/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/iOS/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Examples/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/iOS/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Examples/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/iOS/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Examples/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/iOS/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Examples/Connect/Connect/AppDelegate.swift b/Examples/iOS/Connect/Connect/AppDelegate.swift similarity index 100% rename from Examples/Connect/Connect/AppDelegate.swift rename to Examples/iOS/Connect/Connect/AppDelegate.swift diff --git a/Examples/Connect/Connect/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/iOS/Connect/Connect/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Examples/Connect/Connect/Assets.xcassets/AccentColor.colorset/Contents.json rename to Examples/iOS/Connect/Connect/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Examples/Connect/Connect/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/iOS/Connect/Connect/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Examples/Connect/Connect/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Examples/iOS/Connect/Connect/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Examples/Connect/Connect/Assets.xcassets/Contents.json b/Examples/iOS/Connect/Connect/Assets.xcassets/Contents.json similarity index 100% rename from Examples/Connect/Connect/Assets.xcassets/Contents.json rename to Examples/iOS/Connect/Connect/Assets.xcassets/Contents.json diff --git a/Examples/Connect/Connect/ContentView.swift b/Examples/iOS/Connect/Connect/ContentView.swift similarity index 100% rename from Examples/Connect/Connect/ContentView.swift rename to Examples/iOS/Connect/Connect/ContentView.swift diff --git a/Examples/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Contents.json b/Examples/iOS/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Contents.json similarity index 100% rename from Examples/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Contents.json rename to Examples/iOS/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Contents.json diff --git a/Examples/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Version.json b/Examples/iOS/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Version.json similarity index 100% rename from Examples/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Version.json rename to Examples/iOS/Connect/Connect/Experience.rcproject/Library/ProjectLibrary/Version.json diff --git a/Examples/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 b/Examples/iOS/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 similarity index 100% rename from Examples/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 rename to Examples/iOS/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 diff --git a/Examples/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square b/Examples/iOS/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square similarity index 100% rename from Examples/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square rename to Examples/iOS/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square diff --git a/Examples/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide b/Examples/iOS/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide similarity index 100% rename from Examples/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide rename to Examples/iOS/Connect/Connect/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide diff --git a/Examples/Connect/Connect/Experience.rcproject/com.apple.RCFoundation.Project b/Examples/iOS/Connect/Connect/Experience.rcproject/com.apple.RCFoundation.Project similarity index 100% rename from Examples/Connect/Connect/Experience.rcproject/com.apple.RCFoundation.Project rename to Examples/iOS/Connect/Connect/Experience.rcproject/com.apple.RCFoundation.Project diff --git a/Examples/Connect/Connect/Info.plist b/Examples/iOS/Connect/Connect/Info.plist similarity index 100% rename from Examples/Connect/Connect/Info.plist rename to Examples/iOS/Connect/Connect/Info.plist diff --git a/Examples/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/iOS/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from Examples/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json rename to Examples/iOS/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/Examples/IntricateSetup/IntricateSetup.xcodeproj/project.pbxproj b/Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.pbxproj similarity index 100% rename from Examples/IntricateSetup/IntricateSetup.xcodeproj/project.pbxproj rename to Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.pbxproj diff --git a/Examples/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Examples/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Examples/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Examples/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Examples/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from Examples/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to Examples/iOS/IntricateSetup/IntricateSetup.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Examples/IntricateSetup/IntricateSetup/AppDelegate.swift b/Examples/iOS/IntricateSetup/IntricateSetup/AppDelegate.swift similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/AppDelegate.swift rename to Examples/iOS/IntricateSetup/IntricateSetup/AppDelegate.swift diff --git a/Examples/IntricateSetup/IntricateSetup/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/iOS/IntricateSetup/IntricateSetup/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/Assets.xcassets/AccentColor.colorset/Contents.json rename to Examples/iOS/IntricateSetup/IntricateSetup/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Examples/IntricateSetup/IntricateSetup/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/iOS/IntricateSetup/IntricateSetup/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Examples/iOS/IntricateSetup/IntricateSetup/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Examples/IntricateSetup/IntricateSetup/Assets.xcassets/Contents.json b/Examples/iOS/IntricateSetup/IntricateSetup/Assets.xcassets/Contents.json similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/Assets.xcassets/Contents.json rename to Examples/iOS/IntricateSetup/IntricateSetup/Assets.xcassets/Contents.json diff --git a/Examples/IntricateSetup/IntricateSetup/ContentView.swift b/Examples/iOS/IntricateSetup/IntricateSetup/ContentView.swift similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/ContentView.swift rename to Examples/iOS/IntricateSetup/IntricateSetup/ContentView.swift diff --git a/Examples/IntricateSetup/IntricateSetup/Info.plist b/Examples/iOS/IntricateSetup/IntricateSetup/Info.plist similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/Info.plist rename to Examples/iOS/IntricateSetup/IntricateSetup/Info.plist diff --git a/Examples/IntricateSetup/IntricateSetup/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/iOS/IntricateSetup/IntricateSetup/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from Examples/IntricateSetup/IntricateSetup/Preview Content/Preview Assets.xcassets/Contents.json rename to Examples/iOS/IntricateSetup/IntricateSetup/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/Examples/IntricateSetup/MyLibrary/.gitignore b/Examples/iOS/IntricateSetup/MyLibrary/.gitignore similarity index 100% rename from Examples/IntricateSetup/MyLibrary/.gitignore rename to Examples/iOS/IntricateSetup/MyLibrary/.gitignore diff --git a/Examples/IntricateSetup/MyLibrary/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/iOS/IntricateSetup/MyLibrary/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Examples/IntricateSetup/MyLibrary/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/iOS/IntricateSetup/MyLibrary/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Examples/IntricateSetup/MyLibrary/Package.swift b/Examples/iOS/IntricateSetup/MyLibrary/Package.swift similarity index 94% rename from Examples/IntricateSetup/MyLibrary/Package.swift rename to Examples/iOS/IntricateSetup/MyLibrary/Package.swift index 126f7c7..8a6d97b 100644 --- a/Examples/IntricateSetup/MyLibrary/Package.swift +++ b/Examples/iOS/IntricateSetup/MyLibrary/Package.swift @@ -16,7 +16,7 @@ let package = Package( ], dependencies: [ //.package(url: "https://github.com/monstar-lab-oss/reality-check", from: "0.0.1"), - .package(name: "reality-check", path: "../../.."), + .package(name: "reality-check", path: "../../../.."), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. diff --git a/Examples/IntricateSetup/MyLibrary/Sources/MyLibrary/MyLibrary.swift b/Examples/iOS/IntricateSetup/MyLibrary/Sources/MyLibrary/MyLibrary.swift similarity index 100% rename from Examples/IntricateSetup/MyLibrary/Sources/MyLibrary/MyLibrary.swift rename to Examples/iOS/IntricateSetup/MyLibrary/Sources/MyLibrary/MyLibrary.swift diff --git a/Examples/IntricateSetup/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift b/Examples/iOS/IntricateSetup/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift similarity index 100% rename from Examples/IntricateSetup/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift rename to Examples/iOS/IntricateSetup/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift diff --git a/Examples/iOS/Package.swift b/Examples/iOS/Package.swift new file mode 100644 index 0000000..52a8fbe --- /dev/null +++ b/Examples/iOS/Package.swift @@ -0,0 +1,11 @@ +// swift-tools-version:5.2 + +// Leave blank. This is only here so that Xcode doesn't display it. + +import PackageDescription + +let package = Package( + name: "ios-examples", + products: [], + targets: [] +) diff --git a/Examples/Testbed/Testbed.xcodeproj/project.pbxproj b/Examples/iOS/Testbed/Testbed.xcodeproj/project.pbxproj similarity index 100% rename from Examples/Testbed/Testbed.xcodeproj/project.pbxproj rename to Examples/iOS/Testbed/Testbed.xcodeproj/project.pbxproj diff --git a/Examples/Testbed/Testbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/iOS/Testbed/Testbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Examples/Testbed/Testbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/iOS/Testbed/Testbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Examples/Testbed/Testbed.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/iOS/Testbed/Testbed.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Examples/Testbed/Testbed.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/iOS/Testbed/Testbed.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Examples/Testbed/Testbed/AppDelegate.swift b/Examples/iOS/Testbed/Testbed/AppDelegate.swift similarity index 100% rename from Examples/Testbed/Testbed/AppDelegate.swift rename to Examples/iOS/Testbed/Testbed/AppDelegate.swift diff --git a/Examples/Testbed/Testbed/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/iOS/Testbed/Testbed/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Examples/Testbed/Testbed/Assets.xcassets/AccentColor.colorset/Contents.json rename to Examples/iOS/Testbed/Testbed/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Examples/Testbed/Testbed/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/iOS/Testbed/Testbed/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Examples/Testbed/Testbed/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Examples/iOS/Testbed/Testbed/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Examples/Testbed/Testbed/Assets.xcassets/Contents.json b/Examples/iOS/Testbed/Testbed/Assets.xcassets/Contents.json similarity index 100% rename from Examples/Testbed/Testbed/Assets.xcassets/Contents.json rename to Examples/iOS/Testbed/Testbed/Assets.xcassets/Contents.json diff --git a/Examples/Testbed/Testbed/ContentView.swift b/Examples/iOS/Testbed/Testbed/ContentView.swift similarity index 100% rename from Examples/Testbed/Testbed/ContentView.swift rename to Examples/iOS/Testbed/Testbed/ContentView.swift diff --git a/Examples/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Contents.json b/Examples/iOS/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Contents.json similarity index 100% rename from Examples/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Contents.json rename to Examples/iOS/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Contents.json diff --git a/Examples/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Version.json b/Examples/iOS/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Version.json similarity index 100% rename from Examples/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Version.json rename to Examples/iOS/Testbed/Testbed/Experience.rcproject/Library/ProjectLibrary/Version.json diff --git a/Examples/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 b/Examples/iOS/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 similarity index 100% rename from Examples/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 rename to Examples/iOS/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 diff --git a/Examples/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square b/Examples/iOS/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square similarity index 100% rename from Examples/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square rename to Examples/iOS/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square diff --git a/Examples/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide b/Examples/iOS/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide similarity index 100% rename from Examples/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide rename to Examples/iOS/Testbed/Testbed/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide diff --git a/Examples/Testbed/Testbed/Experience.rcproject/com.apple.RCFoundation.Project b/Examples/iOS/Testbed/Testbed/Experience.rcproject/com.apple.RCFoundation.Project similarity index 100% rename from Examples/Testbed/Testbed/Experience.rcproject/com.apple.RCFoundation.Project rename to Examples/iOS/Testbed/Testbed/Experience.rcproject/com.apple.RCFoundation.Project diff --git a/Examples/Testbed/Testbed/Info.plist b/Examples/iOS/Testbed/Testbed/Info.plist similarity index 100% rename from Examples/Testbed/Testbed/Info.plist rename to Examples/iOS/Testbed/Testbed/Info.plist diff --git a/Examples/Testbed/Testbed/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/iOS/Testbed/Testbed/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from Examples/Testbed/Testbed/Preview Content/Preview Assets.xcassets/Contents.json rename to Examples/iOS/Testbed/Testbed/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/Examples/Testbed/Testbed/aerodynamics_workshop_4k.exr b/Examples/iOS/Testbed/Testbed/aerodynamics_workshop_4k.exr similarity index 100% rename from Examples/Testbed/Testbed/aerodynamics_workshop_4k.exr rename to Examples/iOS/Testbed/Testbed/aerodynamics_workshop_4k.exr diff --git a/Examples/Testbed/Testbed/robot_walk_idle.usdz b/Examples/iOS/Testbed/Testbed/robot_walk_idle.usdz similarity index 100% rename from Examples/Testbed/Testbed/robot_walk_idle.usdz rename to Examples/iOS/Testbed/Testbed/robot_walk_idle.usdz diff --git a/Examples/Testbed/Testbed/toy_car.usdz b/Examples/iOS/Testbed/Testbed/toy_car.usdz similarity index 100% rename from Examples/Testbed/Testbed/toy_car.usdz rename to Examples/iOS/Testbed/Testbed/toy_car.usdz diff --git a/Examples/Testbed/Testbed/wireframe_shader.metal b/Examples/iOS/Testbed/Testbed/wireframe_shader.metal similarity index 100% rename from Examples/Testbed/Testbed/wireframe_shader.metal rename to Examples/iOS/Testbed/Testbed/wireframe_shader.metal diff --git a/Examples/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.pbxproj b/Examples/iOS/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.pbxproj similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.pbxproj rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.pbxproj diff --git a/Examples/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/iOS/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Examples/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/iOS/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/AppDelegate.swift b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/AppDelegate.swift similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/AppDelegate.swift rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/AppDelegate.swift diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/Contents.json b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/Contents.json similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/Contents.json rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Assets.xcassets/Contents.json diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Base.lproj/LaunchScreen.storyboard b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Base.lproj/LaunchScreen.storyboard rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Base.lproj/LaunchScreen.storyboard diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Base.lproj/Main.storyboard b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Base.lproj/Main.storyboard similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Base.lproj/Main.storyboard rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Base.lproj/Main.storyboard diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Contents.json b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Contents.json similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Contents.json rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Contents.json diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Version.json b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Version.json similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Version.json rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/Library/ProjectLibrary/Version.json diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3 diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/square diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/SceneThumbnails/F9610871-0955-494F-A5C3-51D1A281BAB3.thumbnails/wide diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/com.apple.RCFoundation.Project b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/com.apple.RCFoundation.Project similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/com.apple.RCFoundation.Project rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Experience.rcproject/com.apple.RCFoundation.Project diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/Info.plist b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/Info.plist similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/Info.plist rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/Info.plist diff --git a/Examples/UIKitStoryboard/UIKitStoryboard/ViewController.swift b/Examples/iOS/UIKitStoryboard/UIKitStoryboard/ViewController.swift similarity index 100% rename from Examples/UIKitStoryboard/UIKitStoryboard/ViewController.swift rename to Examples/iOS/UIKitStoryboard/UIKitStoryboard/ViewController.swift diff --git a/Examples/visionOS/Connect/Connect.xcodeproj/project.pbxproj b/Examples/visionOS/Connect/Connect.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f794ea1 --- /dev/null +++ b/Examples/visionOS/Connect/Connect.xcodeproj/project.pbxproj @@ -0,0 +1,386 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 4023972E2AA4B89E00D6B666 /* RealityKitContent in Frameworks */ = {isa = PBXBuildFile; productRef = 4023972D2AA4B89E00D6B666 /* RealityKitContent */; }; + 402397302AA4B89E00D6B666 /* ConnectApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4023972F2AA4B89E00D6B666 /* ConnectApp.swift */; }; + 402397322AA4B89E00D6B666 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402397312AA4B89E00D6B666 /* ContentView.swift */; }; + 402397342AA4B89E00D6B666 /* ImmersiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402397332AA4B89E00D6B666 /* ImmersiveView.swift */; }; + 402397362AA4B89F00D6B666 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 402397352AA4B89F00D6B666 /* Assets.xcassets */; }; + 402397392AA4B89F00D6B666 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 402397382AA4B89F00D6B666 /* Preview Assets.xcassets */; }; + 402397422AA4B90A00D6B666 /* RealityCheckConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 402397412AA4B90A00D6B666 /* RealityCheckConnect */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 402397282AA4B89E00D6B666 /* Connect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Connect.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4023972C2AA4B89E00D6B666 /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = ""; }; + 4023972F2AA4B89E00D6B666 /* ConnectApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectApp.swift; sourceTree = ""; }; + 402397312AA4B89E00D6B666 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 402397332AA4B89E00D6B666 /* ImmersiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmersiveView.swift; sourceTree = ""; }; + 402397352AA4B89F00D6B666 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 402397382AA4B89F00D6B666 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 4023973A2AA4B89F00D6B666 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 402397252AA4B89E00D6B666 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4023972E2AA4B89E00D6B666 /* RealityKitContent in Frameworks */, + 402397422AA4B90A00D6B666 /* RealityCheckConnect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4023971F2AA4B89E00D6B666 = { + isa = PBXGroup; + children = ( + 4023972A2AA4B89E00D6B666 /* Connect */, + 4023972B2AA4B89E00D6B666 /* Packages */, + 402397292AA4B89E00D6B666 /* Products */, + 402397402AA4B90A00D6B666 /* Frameworks */, + ); + sourceTree = ""; + }; + 402397292AA4B89E00D6B666 /* Products */ = { + isa = PBXGroup; + children = ( + 402397282AA4B89E00D6B666 /* Connect.app */, + ); + name = Products; + sourceTree = ""; + }; + 4023972A2AA4B89E00D6B666 /* Connect */ = { + isa = PBXGroup; + children = ( + 4023972F2AA4B89E00D6B666 /* ConnectApp.swift */, + 402397312AA4B89E00D6B666 /* ContentView.swift */, + 402397332AA4B89E00D6B666 /* ImmersiveView.swift */, + 402397352AA4B89F00D6B666 /* Assets.xcassets */, + 4023973A2AA4B89F00D6B666 /* Info.plist */, + 402397372AA4B89F00D6B666 /* Preview Content */, + ); + path = Connect; + sourceTree = ""; + }; + 4023972B2AA4B89E00D6B666 /* Packages */ = { + isa = PBXGroup; + children = ( + 4023972C2AA4B89E00D6B666 /* RealityKitContent */, + ); + path = Packages; + sourceTree = ""; + }; + 402397372AA4B89F00D6B666 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 402397382AA4B89F00D6B666 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 402397402AA4B90A00D6B666 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 402397272AA4B89E00D6B666 /* Connect */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4023973D2AA4B89F00D6B666 /* Build configuration list for PBXNativeTarget "Connect" */; + buildPhases = ( + 402397242AA4B89E00D6B666 /* Sources */, + 402397252AA4B89E00D6B666 /* Frameworks */, + 402397262AA4B89E00D6B666 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Connect; + packageProductDependencies = ( + 4023972D2AA4B89E00D6B666 /* RealityKitContent */, + 402397412AA4B90A00D6B666 /* RealityCheckConnect */, + ); + productName = Connect; + productReference = 402397282AA4B89E00D6B666 /* Connect.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 402397202AA4B89E00D6B666 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + 402397272AA4B89E00D6B666 = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = 402397232AA4B89E00D6B666 /* Build configuration list for PBXProject "Connect" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4023971F2AA4B89E00D6B666; + productRefGroup = 402397292AA4B89E00D6B666 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 402397272AA4B89E00D6B666 /* Connect */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 402397262AA4B89E00D6B666 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 402397392AA4B89F00D6B666 /* Preview Assets.xcassets in Resources */, + 402397362AA4B89F00D6B666 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 402397242AA4B89E00D6B666 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 402397322AA4B89E00D6B666 /* ContentView.swift in Sources */, + 402397302AA4B89E00D6B666 /* ConnectApp.swift in Sources */, + 402397342AA4B89E00D6B666 /* ImmersiveView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4023973B2AA4B89F00D6B666 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = xros; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Debug; + }; + 4023973C2AA4B89F00D6B666 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = xros; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Release; + }; + 4023973E2AA4B89F00D6B666 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Connect/Preview Content\""; + DEVELOPMENT_TEAM = M92A6H7EPZ; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.Connect; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Debug; + }; + 4023973F2AA4B89F00D6B666 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Connect/Preview Content\""; + DEVELOPMENT_TEAM = M92A6H7EPZ; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.Connect; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 402397232AA4B89E00D6B666 /* Build configuration list for PBXProject "Connect" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4023973B2AA4B89F00D6B666 /* Debug */, + 4023973C2AA4B89F00D6B666 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4023973D2AA4B89F00D6B666 /* Build configuration list for PBXNativeTarget "Connect" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4023973E2AA4B89F00D6B666 /* Debug */, + 4023973F2AA4B89F00D6B666 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4023972D2AA4B89E00D6B666 /* RealityKitContent */ = { + isa = XCSwiftPackageProductDependency; + productName = RealityKitContent; + }; + 402397412AA4B90A00D6B666 /* RealityCheckConnect */ = { + isa = XCSwiftPackageProductDependency; + productName = RealityCheckConnect; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 402397202AA4B89E00D6B666 /* Project object */; +} diff --git a/Examples/visionOS/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/visionOS/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Examples/visionOS/Connect/Connect.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/visionOS/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/visionOS/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Examples/visionOS/Connect/Connect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Examples/Connect/Connect.xcodeproj/xcshareddata/xcschemes/Connect.xcscheme b/Examples/visionOS/Connect/Connect.xcodeproj/xcshareddata/xcschemes/Connect (visionOS).xcscheme similarity index 93% rename from Examples/Connect/Connect.xcodeproj/xcshareddata/xcschemes/Connect.xcscheme rename to Examples/visionOS/Connect/Connect.xcodeproj/xcshareddata/xcschemes/Connect (visionOS).xcscheme index 67b0920..4245abc 100644 --- a/Examples/Connect/Connect.xcodeproj/xcshareddata/xcschemes/Connect.xcscheme +++ b/Examples/visionOS/Connect/Connect.xcodeproj/xcshareddata/xcschemes/Connect (visionOS).xcscheme @@ -14,7 +14,7 @@ buildForAnalyzing = "YES"> @@ -43,7 +43,7 @@ runnableDebuggingMode = "0"> @@ -60,7 +60,7 @@ runnableDebuggingMode = "0"> diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0c7eecb --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/RealityCheckConnect/Media.xcassets/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json similarity index 100% rename from Sources/RealityCheckConnect/Media.xcassets/Contents.json rename to Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 0000000..950af4d --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + }, + { + "filename" : "Back.solidimagestacklayer" + } + ] +} diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0c7eecb --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0c7eecb --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/Connect/Connect/Assets.xcassets/Contents.json b/Examples/visionOS/Connect/Connect/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/visionOS/Connect/Connect/ConnectApp.swift b/Examples/visionOS/Connect/Connect/ConnectApp.swift new file mode 100644 index 0000000..e1190c1 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/ConnectApp.swift @@ -0,0 +1,26 @@ +// +// ConnectApp.swift +// Connect +// +// Created by Cristian Díaz on 03.09.23. +// + +import RealityCheckConnect +import SwiftUI + +@main +struct ConnectApp: App { + @State var realityCheckConnectModel = RealityCheckConnectViewModel() + + var body: some Scene { + WindowGroup { + ContentView() + .environment(realityCheckConnectModel) + } + + ImmersiveSpace(id: "ImmersiveSpace") { + ImmersiveView() + .environment(realityCheckConnectModel) + } + } +} diff --git a/Examples/visionOS/Connect/Connect/ContentView.swift b/Examples/visionOS/Connect/Connect/ContentView.swift new file mode 100644 index 0000000..b177b58 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/ContentView.swift @@ -0,0 +1,66 @@ +// +// ContentView.swift +// Connect +// +// Created by Cristian Díaz on 03.09.23. +// + +import SwiftUI +import RealityCheckConnect +import RealityKit +import RealityKitContent + +struct ContentView: View { + + @State private var showImmersiveSpace = false + @State private var immersiveSpaceIsShown = false + + @Environment(\.openImmersiveSpace) var openImmersiveSpace + @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace + + @Environment(RealityCheckConnectViewModel.self) + var realityCheckConnectModel + + var body: some View { + VStack { + Model3D(named: "Scene", bundle: realityKitContentBundle) + .padding(.bottom, 50) + .realityCheck() + + Button("Start Window video stream") { + Task { + await realityCheckConnectModel.startVideoStreaming() + } + } + + Text("Hello, world!") + + Toggle("Show Immersive Space", isOn: $showImmersiveSpace) + .toggleStyle(.button) + .padding(.top, 50) + } + .padding() + .onChange(of: showImmersiveSpace) { _, newValue in + Task { + if newValue { + switch await openImmersiveSpace(id: "ImmersiveSpace") { + case .opened: + immersiveSpaceIsShown = true + case .error, .userCancelled: + fallthrough + @unknown default: + immersiveSpaceIsShown = false + showImmersiveSpace = false + } + } else if immersiveSpaceIsShown { + await dismissImmersiveSpace() + immersiveSpaceIsShown = false + } + } + } + } +} + +#Preview(windowStyle: .automatic) { + ContentView() +} diff --git a/Examples/visionOS/Connect/Connect/ImmersiveView.swift b/Examples/visionOS/Connect/Connect/ImmersiveView.swift new file mode 100644 index 0000000..82b6245 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/ImmersiveView.swift @@ -0,0 +1,58 @@ +// +// ImmersiveView.swift +// Connect +// +// Created by Cristian Díaz on 03.09.23. +// + +import SwiftUI +import RealityKit +import RealityKitContent +import RealityCheckConnect + +struct ImmersiveView: View { + + @Environment(RealityCheckConnectViewModel.self) + var realityCheckConnectModel + + let attachmentID = "attachmentID" + + var body: some View { + VStack { + // RealityView { content in + // // Add the initial RealityKit content + // if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) { + // content.add(scene) + // } + // } + + + RealityView { content, attachments in + if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) { + content.add(scene) + } + if let sceneAttachment = attachments.entity(for: attachmentID) { + sceneAttachment.position = .one + content.add(sceneAttachment) + } + + } update: { content, attachments in + print(attachments) + } attachments: { + Attachment(id: attachmentID) { + Button("Start Immersive video stream") { + Task { + await realityCheckConnectModel.startVideoStreaming() + } + } + } + } + // .realityCheck() + } + } +} + +#Preview { + ImmersiveView() + .previewLayout(.sizeThatFits) +} diff --git a/Examples/visionOS/Connect/Connect/Info.plist b/Examples/visionOS/Connect/Connect/Info.plist new file mode 100644 index 0000000..6cb9bc1 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Info.plist @@ -0,0 +1,20 @@ + + + + + NSBonjourServices + + _reality-check._tcp + _reality-check._udp + + UIApplicationSceneManifest + + UIApplicationPreferredDefaultSceneSessionRole + UIWindowSceneSessionRoleApplication + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + + diff --git a/Examples/visionOS/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/visionOS/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/Connect/Connect/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json new file mode 100644 index 0000000..35c2d94 --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json @@ -0,0 +1,11 @@ +{ + "pathsToIds" : { + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "CB766F92-EE55-4A63-9401-E7B8C009764D", + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "65F6F990-A780-4474-B78B-572E0E4E273D", + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "0A9B4653-B11E-4D6A-850E-C6FCB621626C", + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Untitled Scene.usda" : "D560BB77-AAF3-4BDE-B7C4-989332A4688B", + "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "66168B71-AB05-424E-8B6C-D33D6E61B08F", + "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "AF09ED6F-1707-48FD-8720-65B998362C09", + "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "D66134B1-3681-4A8E-AFE5-29F257229F3B" + } +} \ No newline at end of file diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json new file mode 100644 index 0000000..f3d44aa --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json @@ -0,0 +1,242 @@ +{ + "0A9B4653-B11E-4D6A-850E-C6FCB621626C" : { + "cameraTransform" : [ + 1, + 0, + 0, + 0, + 0, + 0.86602545, + -0.49999994, + 0, + 0, + 0.49999994, + 0.86602545, + 0, + 0.0035969093, + 0.35542378, + 0.62919164, + 1 + ], + "objectMetadataList" : [ + [ + "0A9B4653-B11E-4D6A-850E-C6FCB621626C", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "65F6F990-A780-4474-B78B-572E0E4E273D" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.86602545, + -0.49999988, + 0, + 0, + 0.49999988, + 0.86602545, + 0, + 1.1972517e-08, + 2.6179132, + 0.43191218, + 1 + ], + "objectMetadataList" : [ + [ + "65F6F990-A780-4474-B78B-572E0E4E273D", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "66168B71-AB05-424E-8B6C-D33D6E61B08F" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.8660254, + -0.5, + 0, + 0, + 0.5, + 0.8660254, + 0, + 0, + 0.23875366, + 0.4135335, + 1 + ], + "objectMetadataList" : [ + [ + "66168B71-AB05-424E-8B6C-D33D6E61B08F", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "AF09ED6F-1707-48FD-8720-65B998362C09" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.7071069, + -0.7071067, + 0, + 0, + 0.7071067, + 0.7071069, + 0, + 0, + 2.8836339, + -0.107588194, + 1 + ], + "objectMetadataList" : [ + [ + "AF09ED6F-1707-48FD-8720-65B998362C09", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "AF09ED6F-1707-48FD-8720-65B998362C09", + "Root", + "Sphere_Left" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "AF09ED6F-1707-48FD-8720-65B998362C09", + "Root", + "Sphere_Right" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "CB766F92-EE55-4A63-9401-E7B8C009764D" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.8660253, + -0.5000001, + 0, + 0, + 0.5000001, + 0.8660253, + 0, + 0, + 0.27093494, + 0.4692731, + 1 + ], + "objectMetadataList" : [ + [ + "CB766F92-EE55-4A63-9401-E7B8C009764D", + "Root", + "GridMaterial" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "CB766F92-EE55-4A63-9401-E7B8C009764D", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "D560BB77-AAF3-4BDE-B7C4-989332A4688B" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.8660253, + -0.5000001, + 0, + 0, + 0.5000001, + 0.8660253, + 0, + 0, + 0.27093494, + 0.4692731, + 1 + ], + "objectMetadataList" : [ + + ] + }, + "D66134B1-3681-4A8E-AFE5-29F257229F3B" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.7071069, + -0.7071067, + 0, + 0, + 0.7071067, + 0.7071069, + 0, + 0, + 0.26894823, + 0.26934713, + 1 + ], + "objectMetadataList" : [ + [ + "D66134B1-3681-4A8E-AFE5-29F257229F3B", + "Root", + "GridMaterial", + "GridMaterial" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "D66134B1-3681-4A8E-AFE5-29F257229F3B", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + } +} \ No newline at end of file diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata new file mode 100644 index 0000000..6dea95c --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata @@ -0,0 +1,17 @@ +{ + "cameraPresets" : { + + }, + "secondaryToolbarData" : { + "isGridVisible" : true, + "sceneReverbPreset" : -1 + }, + "unitDefaults" : { + "°" : "°", + "kg" : "g", + "m" : "cm", + "m\/s" : "m\/s", + "m\/s²" : "m\/s²", + "s" : "s" + } +} \ No newline at end of file diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Package.swift b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.swift new file mode 100644 index 0000000..d043ae1 --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version:5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "RealityKitContent", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "RealityKitContent", + targets: ["RealityKitContent"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "RealityKitContent", + dependencies: []), + ] +) \ No newline at end of file diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/README.md b/Examples/visionOS/Connect/Packages/RealityKitContent/README.md new file mode 100644 index 0000000..486b575 --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/README.md @@ -0,0 +1,3 @@ +# RealityKitContent + +A description of this package. \ No newline at end of file diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda new file mode 100644 index 0000000..37232ff --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda @@ -0,0 +1,50 @@ +#usda 1.0 +( + defaultPrim = "Root" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "Root" +{ + reorder nameChildren = ["Sphere_Left", "Sphere_Right", "_GridMaterial"] + def Sphere "Sphere_Right" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + rel material:binding = ( + bindMaterialAs = "weakerThanDescendants" + ) + double radius = 0.1 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + float3 xformOp:translate = (0.5, 1.5, -1.5) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "Sphere_Left" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + rel material:binding = ( + bindMaterialAs = "weakerThanDescendants" + ) + double radius = 0.1 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + float3 xformOp:translate = (-0.5, 1.5, -1.5) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def "_GridMaterial" ( + active = true + prepend references = @_GridMaterial.usda@ + ) + { + float3 xformOp:scale = (1, 1, 1) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } +} + diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda new file mode 100644 index 0000000..0600de4 --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda @@ -0,0 +1,59 @@ +#usda 1.0 +( + defaultPrim = "Root" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "Root" +{ + reorder nameChildren = ["GridMaterial", "Sphere"] + rel material:binding = None ( + bindMaterialAs = "weakerThanDescendants" + ) + + def Sphere "Sphere" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + rel material:binding = ( + bindMaterialAs = "weakerThanDescendants" + ) + double radius = 0.05 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + float3 xformOp:translate = (0, 0, 0.0004) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def RealityKitComponent "Collider" + { + uint group = 1 + uniform token info:id = "RealityKit.Collider" + uint mask = 4294967295 + token type = "Default" + + def RealityKitStruct "Shape" + { + float3 extent = (0.2, 0.2, 0.2) + float radius = 0.05 + token shapeType = "Sphere" + } + } + + def RealityKitComponent "InputTarget" + { + uniform token info:id = "RealityKit.InputTarget" + } + } + + def "_GridMaterial" ( + active = true + prepend references = @_GridMaterial.usda@ + ) + { + float3 xformOp:scale = (1, 1, 1) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } +} + diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_GridMaterial.usda b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_GridMaterial.usda new file mode 100644 index 0000000..b7afd02 --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_GridMaterial.usda @@ -0,0 +1,216 @@ +#usda 1.0 +( + defaultPrim = "Root" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "Root" +{ + def Material "GridMaterial" + { + reorder nameChildren = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "DefaultSurfaceShader", "MaterialXPreviewSurface", "Texcoord", "Add", "Multiply", "Fractional", "LineCounts", "Multiply_1", "Separate2", "Separate2_1", "Ifgreater", "Ifgreater_1", "Max", "Background_Color"] + token outputs:mtlx:surface.connect = + token outputs:realitykit:vertex + token outputs:surface + float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (2222, 300.5) + float2 ui:nodegraph:realitykit:subgraphOutputs:size = (182, 89) + int ui:nodegraph:realitykit:subgraphOutputs:stackingOrder = 749 + + def Shader "DefaultSurfaceShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (1, 1, 1) + float inputs:roughness = 0.75 + token outputs:surface + } + + def Shader "MaterialXPreviewSurface" + { + uniform token info:id = "ND_UsdPreviewSurface_surfaceshader" + float inputs:clearcoat + float inputs:clearcoatRoughness + color3f inputs:diffuseColor.connect = + color3f inputs:emissiveColor + float inputs:ior + float inputs:metallic = 0.15 + float3 inputs:normal + float inputs:occlusion + float inputs:opacity + float inputs:opacityThreshold + float inputs:roughness = 0.5 + token outputs:out + float2 ui:nodegraph:node:pos = (1967, 300.5) + float2 ui:nodegraph:node:size = (208, 297) + int ui:nodegraph:node:stackingOrder = 870 + string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["Advanced"] + } + + def Shader "Texcoord" + { + uniform token info:id = "ND_texcoord_vector2" + float2 outputs:out + float2 ui:nodegraph:node:pos = (94.14453, 35.29297) + float2 ui:nodegraph:node:size = (182, 43) + int ui:nodegraph:node:stackingOrder = 1358 + } + + def Shader "Multiply" + { + uniform token info:id = "ND_multiply_vector2" + float2 inputs:in1.connect = + float2 inputs:in2 = (32, 15) + float2 inputs:in2.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (275.64453, 47.29297) + float2 ui:nodegraph:node:size = (61, 36) + int ui:nodegraph:node:stackingOrder = 1348 + string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:in2"] + } + + def Shader "Fractional" + { + uniform token info:id = "ND_realitykit_fractional_vector2" + float2 inputs:in.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (440.5, 49.5) + float2 ui:nodegraph:node:size = (155, 99) + int ui:nodegraph:node:stackingOrder = 1345 + } + + def Shader "BaseColor" + { + uniform token info:id = "ND_constant_color3" + color3f inputs:value = (0.89737034, 0.89737034, 0.89737034) ( + colorSpace = "Input - Texture - sRGB - sRGB" + ) + color3f inputs:value.connect = None + color3f outputs:out + float2 ui:nodegraph:node:pos = (1537.5977, 363.07812) + float2 ui:nodegraph:node:size = (150, 43) + int ui:nodegraph:node:stackingOrder = 1353 + } + + def Shader "LineColor" + { + uniform token info:id = "ND_constant_color3" + color3f inputs:value = (0.55945957, 0.55945957, 0.55945957) ( + colorSpace = "Input - Texture - sRGB - sRGB" + ) + color3f inputs:value.connect = None + color3f outputs:out + float2 ui:nodegraph:node:pos = (1536.9844, 287.86328) + float2 ui:nodegraph:node:size = (146, 43) + int ui:nodegraph:node:stackingOrder = 1355 + } + + def Shader "LineWidths" + { + uniform token info:id = "ND_combine2_vector2" + float inputs:in1 = 0.1 + float inputs:in2 = 0.1 + float2 outputs:out + float2 ui:nodegraph:node:pos = (443.64453, 233.79297) + float2 ui:nodegraph:node:size = (151, 43) + int ui:nodegraph:node:stackingOrder = 1361 + } + + def Shader "LineCounts" + { + uniform token info:id = "ND_combine2_vector2" + float inputs:in1 = 24 + float inputs:in2 = 12 + float2 outputs:out + float2 ui:nodegraph:node:pos = (94.14453, 138.29297) + float2 ui:nodegraph:node:size = (153, 43) + int ui:nodegraph:node:stackingOrder = 1359 + } + + def Shader "Remap" + { + uniform token info:id = "ND_remap_color3" + color3f inputs:in.connect = + color3f inputs:inhigh.connect = None + color3f inputs:inlow.connect = None + color3f inputs:outhigh.connect = + color3f inputs:outlow.connect = + color3f outputs:out + float2 ui:nodegraph:node:pos = (1755.5, 300.5) + float2 ui:nodegraph:node:size = (95, 171) + int ui:nodegraph:node:stackingOrder = 1282 + string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:outlow"] + } + + def Shader "Separate2" + { + uniform token info:id = "ND_separate2_vector2" + float2 inputs:in.connect = + float outputs:outx + float outputs:outy + float2 ui:nodegraph:node:pos = (1212.6445, 128.91797) + float2 ui:nodegraph:node:size = (116, 117) + int ui:nodegraph:node:stackingOrder = 1363 + } + + def Shader "Combine3" + { + uniform token info:id = "ND_combine3_color3" + float inputs:in1.connect = + float inputs:in2.connect = + float inputs:in3.connect = + color3f outputs:out + float2 ui:nodegraph:node:pos = (1578.1445, 128.91797) + float2 ui:nodegraph:node:size = (146, 54) + int ui:nodegraph:node:stackingOrder = 1348 + } + + def Shader "Range" + { + uniform token info:id = "ND_range_vector2" + bool inputs:doclamp = 1 + float2 inputs:gamma = (2, 2) + float2 inputs:in.connect = + float2 inputs:inhigh.connect = + float2 inputs:inlow = (0.02, 0.02) + float2 inputs:outhigh + float2 inputs:outlow + float2 outputs:out + float2 ui:nodegraph:node:pos = (990.64453, 128.91797) + float2 ui:nodegraph:node:size = (98, 207) + int ui:nodegraph:node:stackingOrder = 1364 + } + + def Shader "Subtract" + { + uniform token info:id = "ND_subtract_vector2" + float2 inputs:in1.connect = + float2 inputs:in2.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (612.64453, 87.04297) + float2 ui:nodegraph:node:size = (63, 36) + int ui:nodegraph:node:stackingOrder = 1348 + } + + def Shader "Absval" + { + uniform token info:id = "ND_absval_vector2" + float2 inputs:in.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (765.64453, 87.04297) + float2 ui:nodegraph:node:size = (123, 43) + int ui:nodegraph:node:stackingOrder = 1348 + } + + def Shader "Min" + { + uniform token info:id = "ND_min_float" + float inputs:in1.connect = + float inputs:in2.connect = + float outputs:out + float2 ui:nodegraph:node:pos = (1388.1445, 128.91797) + float2 ui:nodegraph:node:size = (114, 36) + int ui:nodegraph:node:stackingOrder = 1363 + } + } +} + diff --git a/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift new file mode 100644 index 0000000..5caba4e --- /dev/null +++ b/Examples/visionOS/Connect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift @@ -0,0 +1,4 @@ +import Foundation + +/// Bundle for the RealityKitContent project +public let realityKitContentBundle = Bundle.module diff --git a/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.pbxproj b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3856a9d --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.pbxproj @@ -0,0 +1,517 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 407220E42A936C11009BB134 /* StepByStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407220E32A936C11009BB134 /* StepByStepView.swift */; }; + 407220E82A93BD30009BB134 /* Recipe+Samples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407220E72A93BD30009BB134 /* Recipe+Samples.swift */; }; + 407EAD2C2A9D1DC0007C46AA /* ServingsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407EAD2B2A9D1DC0007C46AA /* ServingsPicker.swift */; }; + 40B512272A9E510F00DC4BBB /* Wall_Kitchen_Clock_50s.usdz in Resources */ = {isa = PBXBuildFile; fileRef = 40B512262A9E510F00DC4BBB /* Wall_Kitchen_Clock_50s.usdz */; }; + 40B5124A2A9E51A900DC4BBB /* ClockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B512492A9E51A900DC4BBB /* ClockView.swift */; }; + 40B70D782AA3B8B400785A9C /* RealityCheckConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 40B70D772AA3B8B400785A9C /* RealityCheckConnect */; }; + 40BB48332A87D61F008422AE /* AppCoreFeatureModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48322A87D61F008422AE /* AppCoreFeatureModel.swift */; }; + 40BB48362A87D6F1008422AE /* Destination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48352A87D6F1008422AE /* Destination.swift */; }; + 40BB48382A87E417008422AE /* GoalsSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48372A87E417008422AE /* GoalsSelector.swift */; }; + 40BB483A2A87E6EB008422AE /* Goal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48392A87E6EB008422AE /* Goal.swift */; }; + 40BB483C2A87F2EC008422AE /* Diet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB483B2A87F2EC008422AE /* Diet.swift */; }; + 40BB483E2A87F431008422AE /* DietSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB483D2A87F431008422AE /* DietSelector.swift */; }; + 40BB48402A87FFC7008422AE /* ServingsCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB483F2A87FFC7008422AE /* ServingsCounter.swift */; }; + 40BB48422A880514008422AE /* AlmostThere.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48412A880514008422AE /* AlmostThere.swift */; }; + 40BB48442A894057008422AE /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48432A894057008422AE /* MainView.swift */; }; + 40BB48462A89449E008422AE /* OnboardingFeatureModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48452A89449E008422AE /* OnboardingFeatureModel.swift */; }; + 40BB48842A8A6C23008422AE /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48832A8A6C23008422AE /* Preferences.swift */; }; + 40BB48872A8A8F4F008422AE /* TodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48862A8A8F4F008422AE /* TodayView.swift */; }; + 40BB48892A8A8FBA008422AE /* Recipe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BB48882A8A8FBA008422AE /* Recipe.swift */; }; + 40CE54A12A8BA3EE002C0FA3 /* RecipeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE54A02A8BA3EE002C0FA3 /* RecipeView.swift */; }; + 40CE54A42A8BF54A002C0FA3 /* StepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE54A32A8BF54A002C0FA3 /* StepView.swift */; }; + 40CE54A62A8BF591002C0FA3 /* StepByStepFeatureModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE54A52A8BF591002C0FA3 /* StepByStepFeatureModel.swift */; }; + 40D4D81D2A9A1E0300E38ABD /* OnboardingStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D4D81C2A9A1E0300E38ABD /* OnboardingStepView.swift */; }; + 40D4D81F2A9A20DF00E38ABD /* PresentationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D4D81E2A9A20DF00E38ABD /* PresentationView.swift */; }; + 40E7521A2A8667990040E74F /* HelloWurstApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E752192A8667990040E74F /* HelloWurstApp.swift */; }; + 40E7521C2A8667990040E74F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E7521B2A8667990040E74F /* ContentView.swift */; }; + 40E7521E2A86679B0040E74F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40E7521D2A86679B0040E74F /* Assets.xcassets */; }; + 40E752212A86679B0040E74F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40E752202A86679B0040E74F /* Preview Assets.xcassets */; }; + 40E752292A8669EA0040E74F /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E752282A8669EA0040E74F /* OnboardingView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 407220E32A936C11009BB134 /* StepByStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepByStepView.swift; sourceTree = ""; }; + 407220E72A93BD30009BB134 /* Recipe+Samples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Recipe+Samples.swift"; sourceTree = ""; }; + 407EAD2B2A9D1DC0007C46AA /* ServingsPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsPicker.swift; sourceTree = ""; }; + 40B512262A9E510F00DC4BBB /* Wall_Kitchen_Clock_50s.usdz */ = {isa = PBXFileReference; lastKnownFileType = file.usdz; path = Wall_Kitchen_Clock_50s.usdz; sourceTree = ""; }; + 40B512492A9E51A900DC4BBB /* ClockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClockView.swift; sourceTree = ""; }; + 40B70D792AA3BB5200785A9C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 40BB48322A87D61F008422AE /* AppCoreFeatureModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoreFeatureModel.swift; sourceTree = ""; }; + 40BB48352A87D6F1008422AE /* Destination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Destination.swift; sourceTree = ""; }; + 40BB48372A87E417008422AE /* GoalsSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalsSelector.swift; sourceTree = ""; }; + 40BB48392A87E6EB008422AE /* Goal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Goal.swift; sourceTree = ""; }; + 40BB483B2A87F2EC008422AE /* Diet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Diet.swift; sourceTree = ""; }; + 40BB483D2A87F431008422AE /* DietSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DietSelector.swift; sourceTree = ""; }; + 40BB483F2A87FFC7008422AE /* ServingsCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsCounter.swift; sourceTree = ""; }; + 40BB48412A880514008422AE /* AlmostThere.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlmostThere.swift; sourceTree = ""; }; + 40BB48432A894057008422AE /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; + 40BB48452A89449E008422AE /* OnboardingFeatureModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFeatureModel.swift; sourceTree = ""; }; + 40BB48832A8A6C23008422AE /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; + 40BB48862A8A8F4F008422AE /* TodayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayView.swift; sourceTree = ""; }; + 40BB48882A8A8FBA008422AE /* Recipe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Recipe.swift; sourceTree = ""; }; + 40CE54A02A8BA3EE002C0FA3 /* RecipeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeView.swift; sourceTree = ""; }; + 40CE54A32A8BF54A002C0FA3 /* StepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepView.swift; sourceTree = ""; }; + 40CE54A52A8BF591002C0FA3 /* StepByStepFeatureModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepByStepFeatureModel.swift; sourceTree = ""; }; + 40D4D81C2A9A1E0300E38ABD /* OnboardingStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStepView.swift; sourceTree = ""; }; + 40D4D81E2A9A20DF00E38ABD /* PresentationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationView.swift; sourceTree = ""; }; + 40E752162A8667990040E74F /* HelloWurst.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWurst.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 40E752192A8667990040E74F /* HelloWurstApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelloWurstApp.swift; sourceTree = ""; }; + 40E7521B2A8667990040E74F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 40E7521D2A86679B0040E74F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 40E752202A86679B0040E74F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 40E752282A8669EA0040E74F /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 40E752132A8667990040E74F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40B70D782AA3B8B400785A9C /* RealityCheckConnect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 40BB48342A87D6E0008422AE /* SharedModels */ = { + isa = PBXGroup; + children = ( + 40BB483B2A87F2EC008422AE /* Diet.swift */, + 40BB48392A87E6EB008422AE /* Goal.swift */, + 40BB48832A8A6C23008422AE /* Preferences.swift */, + 40BB48882A8A8FBA008422AE /* Recipe.swift */, + 407220E72A93BD30009BB134 /* Recipe+Samples.swift */, + ); + path = SharedModels; + sourceTree = ""; + }; + 40BB48852A8A8F1E008422AE /* TodayFeature */ = { + isa = PBXGroup; + children = ( + 40BB48862A8A8F4F008422AE /* TodayView.swift */, + ); + path = TodayFeature; + sourceTree = ""; + }; + 40CE549F2A8BA3DC002C0FA3 /* SharedViews */ = { + isa = PBXGroup; + children = ( + 40CE54A02A8BA3EE002C0FA3 /* RecipeView.swift */, + 407EAD2B2A9D1DC0007C46AA /* ServingsPicker.swift */, + ); + path = SharedViews; + sourceTree = ""; + }; + 40CE54A22A8BF43A002C0FA3 /* StepByStepFeature */ = { + isa = PBXGroup; + children = ( + 40B512262A9E510F00DC4BBB /* Wall_Kitchen_Clock_50s.usdz */, + 40CE54A32A8BF54A002C0FA3 /* StepView.swift */, + 40CE54A52A8BF591002C0FA3 /* StepByStepFeatureModel.swift */, + 407220E32A936C11009BB134 /* StepByStepView.swift */, + 40B512492A9E51A900DC4BBB /* ClockView.swift */, + ); + path = StepByStepFeature; + sourceTree = ""; + }; + 40E7520D2A8667990040E74F = { + isa = PBXGroup; + children = ( + 40E752182A8667990040E74F /* HelloWurst */, + 40E752172A8667990040E74F /* Products */, + ); + sourceTree = ""; + }; + 40E752172A8667990040E74F /* Products */ = { + isa = PBXGroup; + children = ( + 40E752162A8667990040E74F /* HelloWurst.app */, + ); + name = Products; + sourceTree = ""; + }; + 40E752182A8667990040E74F /* HelloWurst */ = { + isa = PBXGroup; + children = ( + 40B70D792AA3BB5200785A9C /* Info.plist */, + 40BB48322A87D61F008422AE /* AppCoreFeatureModel.swift */, + 40E7521B2A8667990040E74F /* ContentView.swift */, + 40BB48352A87D6F1008422AE /* Destination.swift */, + 40E752192A8667990040E74F /* HelloWurstApp.swift */, + 40BB48432A894057008422AE /* MainView.swift */, + 40E7521D2A86679B0040E74F /* Assets.xcassets */, + 40E752272A8669D90040E74F /* OnboardingFeature */, + 40E7521F2A86679B0040E74F /* Preview Content */, + 40BB48342A87D6E0008422AE /* SharedModels */, + 40CE549F2A8BA3DC002C0FA3 /* SharedViews */, + 40CE54A22A8BF43A002C0FA3 /* StepByStepFeature */, + 40BB48852A8A8F1E008422AE /* TodayFeature */, + ); + path = HelloWurst; + sourceTree = ""; + }; + 40E7521F2A86679B0040E74F /* Preview Content */ = { + isa = PBXGroup; + children = ( + 40E752202A86679B0040E74F /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 40E752272A8669D90040E74F /* OnboardingFeature */ = { + isa = PBXGroup; + children = ( + 40BB48412A880514008422AE /* AlmostThere.swift */, + 40BB483D2A87F431008422AE /* DietSelector.swift */, + 40BB48372A87E417008422AE /* GoalsSelector.swift */, + 40BB48452A89449E008422AE /* OnboardingFeatureModel.swift */, + 40E752282A8669EA0040E74F /* OnboardingView.swift */, + 40BB483F2A87FFC7008422AE /* ServingsCounter.swift */, + 40D4D81C2A9A1E0300E38ABD /* OnboardingStepView.swift */, + 40D4D81E2A9A20DF00E38ABD /* PresentationView.swift */, + ); + path = OnboardingFeature; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 40E752152A8667990040E74F /* HelloWurst */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40E752242A86679B0040E74F /* Build configuration list for PBXNativeTarget "HelloWurst" */; + buildPhases = ( + 40E752122A8667990040E74F /* Sources */, + 40E752132A8667990040E74F /* Frameworks */, + 40E752142A8667990040E74F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HelloWurst; + packageProductDependencies = ( + 40B70D772AA3B8B400785A9C /* RealityCheckConnect */, + ); + productName = HelloWurst; + productReference = 40E752162A8667990040E74F /* HelloWurst.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 40E7520E2A8667990040E74F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + 40E752152A8667990040E74F = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = 40E752112A8667990040E74F /* Build configuration list for PBXProject "HelloWurst" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 40E7520D2A8667990040E74F; + packageReferences = ( + 40B70D762AA3B8B400785A9C /* XCLocalSwiftPackageReference "../../../../../../../../../nodes-projects/RealityCheck/source/reality-check" */, + ); + productRefGroup = 40E752172A8667990040E74F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 40E752152A8667990040E74F /* HelloWurst */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 40E752142A8667990040E74F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40B512272A9E510F00DC4BBB /* Wall_Kitchen_Clock_50s.usdz in Resources */, + 40E752212A86679B0040E74F /* Preview Assets.xcassets in Resources */, + 40E7521E2A86679B0040E74F /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 40E752122A8667990040E74F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40CE54A12A8BA3EE002C0FA3 /* RecipeView.swift in Sources */, + 40B5124A2A9E51A900DC4BBB /* ClockView.swift in Sources */, + 40BB48422A880514008422AE /* AlmostThere.swift in Sources */, + 40BB48442A894057008422AE /* MainView.swift in Sources */, + 40E752292A8669EA0040E74F /* OnboardingView.swift in Sources */, + 407220E42A936C11009BB134 /* StepByStepView.swift in Sources */, + 40E7521C2A8667990040E74F /* ContentView.swift in Sources */, + 407220E82A93BD30009BB134 /* Recipe+Samples.swift in Sources */, + 40BB48892A8A8FBA008422AE /* Recipe.swift in Sources */, + 40D4D81D2A9A1E0300E38ABD /* OnboardingStepView.swift in Sources */, + 40D4D81F2A9A20DF00E38ABD /* PresentationView.swift in Sources */, + 40CE54A42A8BF54A002C0FA3 /* StepView.swift in Sources */, + 40BB48362A87D6F1008422AE /* Destination.swift in Sources */, + 40BB48872A8A8F4F008422AE /* TodayView.swift in Sources */, + 40BB483C2A87F2EC008422AE /* Diet.swift in Sources */, + 407EAD2C2A9D1DC0007C46AA /* ServingsPicker.swift in Sources */, + 40BB48382A87E417008422AE /* GoalsSelector.swift in Sources */, + 40BB48332A87D61F008422AE /* AppCoreFeatureModel.swift in Sources */, + 40E7521A2A8667990040E74F /* HelloWurstApp.swift in Sources */, + 40BB48842A8A6C23008422AE /* Preferences.swift in Sources */, + 40CE54A62A8BF591002C0FA3 /* StepByStepFeatureModel.swift in Sources */, + 40BB483A2A87E6EB008422AE /* Goal.swift in Sources */, + 40BB483E2A87F431008422AE /* DietSelector.swift in Sources */, + 40BB48402A87FFC7008422AE /* ServingsCounter.swift in Sources */, + 40BB48462A89449E008422AE /* OnboardingFeatureModel.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 40E752222A86679B0040E74F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 40E752232A86679B0040E74F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 40E752252A86679B0040E74F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"HelloWurst/Preview Content\""; + DEVELOPMENT_TEAM = M92A6H7EPZ; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HelloWurst/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "HelloWurst (P2)"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.HelloWurst.P2; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Debug; + }; + 40E752262A86679B0040E74F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"HelloWurst/Preview Content\""; + DEVELOPMENT_TEAM = M92A6H7EPZ; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HelloWurst/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "HelloWurst (P2)"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.HelloWurst.P2; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 40E752112A8667990040E74F /* Build configuration list for PBXProject "HelloWurst" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40E752222A86679B0040E74F /* Debug */, + 40E752232A86679B0040E74F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40E752242A86679B0040E74F /* Build configuration list for PBXNativeTarget "HelloWurst" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40E752252A86679B0040E74F /* Debug */, + 40E752262A86679B0040E74F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 40B70D762AA3B8B400785A9C /* XCLocalSwiftPackageReference "../../../../../../../../../nodes-projects/RealityCheck/source/reality-check" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../../../../../../../../../nodes-projects/RealityCheck/source/reality-check"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 40B70D772AA3B8B400785A9C /* RealityCheckConnect */ = { + isa = XCSwiftPackageProductDependency; + productName = RealityCheckConnect; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 40E7520E2A8667990040E74F /* Project object */; +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..3a190e6 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,68 @@ +{ + "pins" : [ + { + "identity" : "combine-schedulers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/combine-schedulers", + "state" : { + "revision" : "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", + "version" : "1.0.0" + } + }, + { + "identity" : "devicekit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devicekit/DeviceKit.git", + "state" : { + "revision" : "691fe8112cca20ebf0020a1709d4e0205400311c", + "version" : "5.0.0" + } + }, + { + "identity" : "swift-clocks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-clocks", + "state" : { + "revision" : "d1fd837326aa719bee979bdde1f53cd5797443eb", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-concurrency-extras", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-concurrency-extras", + "state" : { + "revision" : "ea631ce892687f5432a833312292b80db238186a", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "edd66cace818e1b1c6f1b3349bb1d8e00d6f8b01", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-dependencies", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-dependencies", + "state" : { + "revision" : "4e1eb6e28afe723286d8cc60611237ffbddba7c5", + "version" : "1.0.0" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "23cbf2294e350076ea4dbd7d5d047c1e76b03631", + "version" : "1.0.2" + } + } + ], + "version" : 2 +} diff --git a/Examples/Testbed/Testbed.xcodeproj/xcshareddata/xcschemes/Testbed.xcscheme b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/xcshareddata/xcschemes/HelloWurst (P2).xcscheme similarity index 77% rename from Examples/Testbed/Testbed.xcodeproj/xcshareddata/xcschemes/Testbed.xcscheme rename to Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/xcshareddata/xcschemes/HelloWurst (P2).xcscheme index 22a3644..51540df 100644 --- a/Examples/Testbed/Testbed.xcodeproj/xcshareddata/xcschemes/Testbed.xcscheme +++ b/Examples/visionOS/HelloWurst/HelloWurst.xcodeproj/xcshareddata/xcschemes/HelloWurst (P2).xcscheme @@ -14,10 +14,10 @@ buildForAnalyzing = "YES"> + BlueprintIdentifier = "40E752152A8667990040E74F" + BuildableName = "HelloWurst.app" + BlueprintName = "HelloWurst" + ReferencedContainer = "container:HelloWurst.xcodeproj"> @@ -43,10 +43,10 @@ runnableDebuggingMode = "0"> + BlueprintIdentifier = "40E752152A8667990040E74F" + BuildableName = "HelloWurst.app" + BlueprintName = "HelloWurst" + ReferencedContainer = "container:HelloWurst.xcodeproj"> @@ -60,10 +60,10 @@ runnableDebuggingMode = "0"> + BlueprintIdentifier = "40E752152A8667990040E74F" + BuildableName = "HelloWurst.app" + BlueprintName = "HelloWurst" + ReferencedContainer = "container:HelloWurst.xcodeproj"> diff --git a/Examples/visionOS/HelloWurst/HelloWurst/AppCoreFeatureModel.swift b/Examples/visionOS/HelloWurst/HelloWurst/AppCoreFeatureModel.swift new file mode 100644 index 0000000..5cd08e5 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/AppCoreFeatureModel.swift @@ -0,0 +1,71 @@ +import Foundation +import SwiftUI +import UIKit + +@Observable +class AppCoreFeatureModel { + var destination: Destination + var onboardingModel: OnboardingFeatureModel + var isOnboadingPresented: Bool { + get { + self.destination == .onboarding + } + set { + displayMain() + } + } + let recipes: [Recipe] + var selectedRecipe: Recipe? + var isStepByStepPresented: Bool = false + + init( + destination: Destination = .onboarding, + onboardingModel: OnboardingFeatureModel = OnboardingFeatureModel(), + recipes: [Recipe] = [] + ) { + self.destination = destination + self.onboardingModel = onboardingModel + self.recipes = recipes + self.bind() + } + + private func bind() { + self.onboardingModel.onboardingDidComplete = { + self.displayMain() + } + } + + func displayOnboarding() { + destination = .onboarding + } + + func displayMain() { + destination = .main + } + + func displayStepByStep(recipe: Recipe) { + isStepByStepPresented = true + selectedRecipe = recipe + requestLandscape() + } + + func hideStepByStep() { + isStepByStepPresented = false + selectedRecipe = nil + requestPortrait() + } + + private func requestLandscape() { + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { + return + } + windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .landscape)) + } + + private func requestPortrait() { + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { + return + } + windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .portrait)) + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..d90b065 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "color" : { + "platform" : "universal", + "reference" : "systemOrangeColor" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/Contents.json new file mode 100644 index 0000000..92d639b --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "agnieszka-kowalczyk-obMdrL5pFWI-unsplash.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "agnieszka-kowalczyk-obMdrL5pFWI-unsplash@2x.jpg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "agnieszka-kowalczyk-obMdrL5pFWI-unsplash@3x.jpg", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.jpg new file mode 100644 index 0000000..9fcdb42 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash@2x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash@2x.jpg new file mode 100644 index 0000000..dfc887a Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash@2x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash@3x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash@3x.jpg new file mode 100644 index 0000000..a405b6a Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/agnieszka-kowalczyk-obMdrL5pFWI-unsplash.imageset/agnieszka-kowalczyk-obMdrL5pFWI-unsplash@3x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/Contents.json new file mode 100644 index 0000000..96afe7a --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "benjamin-kaufmann-sb3Cv_K14Js-unsplash.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "benjamin-kaufmann-sb3Cv_K14Js-unsplash@2x.jpg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "benjamin-kaufmann-sb3Cv_K14Js-unsplash@3x.jpg", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash.jpg new file mode 100644 index 0000000..8d07241 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash@2x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash@2x.jpg new file mode 100644 index 0000000..f0bf45e Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash@2x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash@3x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash@3x.jpg new file mode 100644 index 0000000..d5d4e20 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/benjamin-kaufmann-sb3Cv_K14Js-unsplash.imageset/benjamin-kaufmann-sb3Cv_K14Js-unsplash@3x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/Contents.json new file mode 100644 index 0000000..12497f9 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "ratul-ghosh-NPrWYa69Mz0-unsplash 1.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ratul-ghosh-NPrWYa69Mz0-unsplash 1@2x.jpg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ratul-ghosh-NPrWYa69Mz0-unsplash 1@3x.jpg", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1.jpg new file mode 100644 index 0000000..6db73a7 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1@2x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1@2x.jpg new file mode 100644 index 0000000..1799075 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1@2x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1@3x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1@3x.jpg new file mode 100644 index 0000000..8ee3e65 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/ratul-ghosh-NPrWYa69Mz0-unsplash.imageset/ratul-ghosh-NPrWYa69Mz0-unsplash 1@3x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/Contents.json new file mode 100644 index 0000000..00b1495 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wursthain.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wursthain@2x.jpg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wursthain@3x.jpg", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain.jpg new file mode 100644 index 0000000..4bc8d86 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain@2x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain@2x.jpg new file mode 100644 index 0000000..d87f774 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain@2x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain@3x.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain@3x.jpg new file mode 100644 index 0000000..c6276c1 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Onboarding/wursthain.imageset/wursthain@3x.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/Contents.json new file mode 100644 index 0000000..6d07164 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "classic-truck.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "classic-truck@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "classic-truck@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck.jpg new file mode 100644 index 0000000..abbbbe5 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck@2x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck@2x.png new file mode 100644 index 0000000..cf13067 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck@2x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck@3x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck@3x.png new file mode 100644 index 0000000..b3623a0 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/classic-truck.imageset/classic-truck@3x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/Contents.json new file mode 100644 index 0000000..cbe8121 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "gingery-orange.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "gingery-orange@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "gingery-orange@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange.jpg new file mode 100644 index 0000000..e4b98ed Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange@2x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange@2x.png new file mode 100644 index 0000000..cb61733 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange@2x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange@3x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange@3x.png new file mode 100644 index 0000000..2c85867 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/gingery-orange.imageset/gingery-orange@3x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/Contents.json new file mode 100644 index 0000000..69320bf --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "green-coconut.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "green-coconut@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "green-coconut@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut.jpg new file mode 100644 index 0000000..911a1c4 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut@2x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut@2x.png new file mode 100644 index 0000000..3f9aac6 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut@2x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut@3x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut@3x.png new file mode 100644 index 0000000..d389bd4 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/green-coconut.imageset/green-coconut@3x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/Contents.json new file mode 100644 index 0000000..eb91239 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "honey-mustard.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "honey-mustard@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "honey-mustard@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard.jpg new file mode 100644 index 0000000..d28dc86 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard@2x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard@2x.png new file mode 100644 index 0000000..ccd5190 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard@2x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard@3x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard@3x.png new file mode 100644 index 0000000..8e92075 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/honey-mustard.imageset/honey-mustard@3x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/Contents.json new file mode 100644 index 0000000..a6bfb6e --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "radish-arugula.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "radish-arugula@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "radish-arugula@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula.jpg b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula.jpg new file mode 100644 index 0000000..2033de8 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula.jpg differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula@2x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula@2x.png new file mode 100644 index 0000000..a3e79ed Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula@2x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula@3x.png b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula@3x.png new file mode 100644 index 0000000..a6244a4 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/Assets.xcassets/Recipes/radish-arugula.imageset/radish-arugula@3x.png differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/ContentView.swift b/Examples/visionOS/HelloWurst/HelloWurst/ContentView.swift new file mode 100644 index 0000000..8a2980f --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/ContentView.swift @@ -0,0 +1,35 @@ +import SwiftUI + +struct ContentView: View { + @Environment(AppCoreFeatureModel.self) private var model + @Environment(Preferences.self) private var preferences + + var body: some View { + @Bindable var model = model + + MainView() + .fullScreenCover(isPresented: $model.isOnboadingPresented) { + OnboardingView() + .environment(model.onboardingModel) + .environment(preferences) + } + } +} + +#Preview { + ContentView() + .environment(AppCoreFeatureModel()) + .environment(Preferences()) + .previewDisplayName("Onboarding") +} + +#Preview { + ContentView() + .environment( + AppCoreFeatureModel( + destination: .main, + recipes: Recipe.samples) + ) + .environment(Preferences()) + .previewDisplayName("Main") +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Destination.swift b/Examples/visionOS/HelloWurst/HelloWurst/Destination.swift new file mode 100644 index 0000000..f1e55cd --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Destination.swift @@ -0,0 +1,6 @@ +import Foundation + +enum Destination { + case onboarding + case main +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/HelloWurstApp.swift b/Examples/visionOS/HelloWurst/HelloWurst/HelloWurstApp.swift new file mode 100644 index 0000000..63603a9 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/HelloWurstApp.swift @@ -0,0 +1,31 @@ +import SwiftUI +import RealityCheckConnect + +@main +struct HelloWurstApp: App { + + @State private + var model = AppCoreFeatureModel(recipes: Recipe.samples) + + @State private + var realityCheckConnectModel = RealityCheckConnectViewModel() + + @State private + var preferences = Preferences() + + var body: some Scene { + WindowGroup { + ContentView() + .environment(model) + .environment(preferences) + } + +#if os(visionOS) + WindowGroup.init(id: "Clock") { + ClockView() + .environment(realityCheckConnectModel) + } + .windowStyle(.volumetric) +#endif + } +} diff --git a/App/macOS/RealityCheck-Info.plist b/Examples/visionOS/HelloWurst/HelloWurst/Info.plist similarity index 100% rename from App/macOS/RealityCheck-Info.plist rename to Examples/visionOS/HelloWurst/HelloWurst/Info.plist diff --git a/Examples/visionOS/HelloWurst/HelloWurst/MainView.swift b/Examples/visionOS/HelloWurst/HelloWurst/MainView.swift new file mode 100644 index 0000000..8c1eced --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/MainView.swift @@ -0,0 +1,28 @@ +import SwiftUI + +struct MainView: View { + var body: some View { + TabView { + TodayView() + .tabItem { Label("Today", systemImage: "sun.horizon.fill") } + + Text("For you") + .tabItem { Label("For you", systemImage: "calendar") } + + Text("Favorites") + .tabItem { Label("Favorites", systemImage: "heart.fill") } + + Text("Shopping") + .tabItem { Label("Shopping", systemImage: "suitcase.fill") } + + Text("Profile") + .tabItem { Label("Profile", systemImage: "person.fill") } + } + } +} + +#Preview { + MainView() + .environment(AppCoreFeatureModel(recipes: Recipe.samples)) + .environment(Preferences()) +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/AlmostThere.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/AlmostThere.swift new file mode 100644 index 0000000..2f8a297 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/AlmostThere.swift @@ -0,0 +1,128 @@ +import SwiftUI + +struct AlmostThere: View { + + @Environment(OnboardingFeatureModel.self) + var model + + @AccessibilityFocusState + var isReadyButtonFocused: Bool + + @State + var progress = 1.0 + + var body: some View { + List { + Section { + VStack { + withAnimation { + Text(model.isReady ? "Ready to go!" : "You're almost there!") + .font(.system(.title, design: .serif)) + .bold() + .multilineTextAlignment(.center) + .animation(.default, value: model.isReady) + .accessibilityAddTraits(.updatesFrequently) + } + + Spacer() + + withAnimation { + VStack(alignment: .leading, spacing: 8) { + let isFirstStepCompleted = progress < 0.8 + Label("Finding recipes for your diet", systemImage: "checkmark.circle.fill") + .foregroundStyle(.primary, isFirstStepCompleted ? Color.accentColor : .secondary) + .opacity(isFirstStepCompleted ? 1 : 0.25) + .animation(.easeInOut, value: progress) + .offset(y: isFirstStepCompleted ? -5 : 0) + + let isSecondStepCompleted = progress < 0.5 + Label("Sorting out ingredients you don't eat", systemImage: "checkmark.circle.fill") + .foregroundStyle(.primary, isSecondStepCompleted ? Color.accentColor : .secondary) + .opacity(isSecondStepCompleted ? 1 : 0.25) + .animation(.easeInOut, value: progress) + .offset(y: isSecondStepCompleted ? -5 : 0) + + let isThirdStepCompleted = progress < 0.2 + Label("Personalizing your recipes", systemImage: "checkmark.circle.fill") + .foregroundStyle(.primary, isThirdStepCompleted ? Color.accentColor: .secondary) + .opacity(isThirdStepCompleted ? 1 : 0.25) + .animation(.easeInOut, value: progress) + .offset(y: isThirdStepCompleted ? -5 : 0) + } + .accessibilityHidden(true) + .padding(.top) + } + } + .listRowSeparator(.hidden) + } header: { + VStack { + Spacer() + .frame(width: .zero, height: 0) + .accessibilityLabel( + """ + "Step 4 of 4" + - Finding recipes for your diet + - Sorting out ingredients you don't eat + - Personalizing your recipes + """ + ) + + Image(decorative: "wursthain") + .resizable() + .scaledToFit() + .clipShape(Circle()) + .shadow(radius: 5) + .overlay { + withAnimation { + Circle() + .trim(from: 0.0, to: progress) + .stroke(style: StrokeStyle(lineWidth: 16.0, lineCap: .round, lineJoin: .round)) + .foregroundColor(.accentColor) + .rotationEffect(Angle(degrees: 270.0)) + .animation(.easeInOut, value: progress) + } + } + } + .padding() + } + } + .listStyle(.plain) + .toolbar { + ToolbarItem(placement: .bottomBar) { + Button( + action: { model.onboardingDidComplete() }, + label: { + Text(model.isReady ? "Finally".uppercased() : "Processing...".uppercased()) + .bold() + .frame(maxWidth: .infinity) + } + ) + .buttonStyle(.borderedProminent) + .controlSize(.large) + .padding() + .disabled(!model.isReady) + .accessibilityAddTraits(.updatesFrequently) + .accessibilityValue(isReadyButtonFocused ? ". Process completed" : "Still processing.") + .accessibilityHint(model.isReady ? "Complete the onboarding process and browse today's recipes tailored to you." : "") + .accessibilityFocused($isReadyButtonFocused) + } + } + .task { + Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in + progress -= Double.random(in: 0.001...0.05) + if progress <= 0 { + timer.invalidate() + model.onboardingIsReady() + isReadyButtonFocused = true + } + } + } + } +} + +#Preview { + NavigationStack { + AlmostThere() + .environment(OnboardingFeatureModel()) + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/DietSelector.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/DietSelector.swift new file mode 100644 index 0000000..0a460dc --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/DietSelector.swift @@ -0,0 +1,91 @@ +import SwiftUI + +struct DietSelector: View { + + @Environment(OnboardingFeatureModel.self) + var model + + @Environment(Preferences.self) + var preferences + + var body: some View { + List { + Section { + ForEach(Diet.allCases) { diet in + let isSelected = diet == preferences.diet + Button( + action: { preferences.diet = diet }, + label: { + HStack { + Label(diet.rawValue, systemImage: diet.systemImage) + if diet.isIncludedInFreeVersion { + Spacer() + Text("Included in free version".uppercased()).font(.caption2) + .padding(3) + .background(RoundedRectangle(cornerRadius: 3).fill(Color.secondary.opacity(0.2))) + } + } + } + ) + .tag(diet) + .listItemTint(isSelected ? Color.primary : .accentColor) + .listRowBackground(isSelected ? Color.accentColor : .clear ) + .listRowSeparator(.hidden) + .accessibilityAction(named: "Continue") { + model.displayServingsCounter() + } + } + } header: { + VStack { + Spacer() + .frame(width: .zero, height: 0) + .accessibilityLabel("Step 2 of 4") + + Text("What's your diet?") + .font(.system(.title, design: .serif)) + .bold() + + Text("The free version includes the first 2 options.") + .fontWeight(.regular) + } + .frame(maxWidth: .infinity) + .foregroundStyle(.primary) + .padding(.bottom, 30) + .multilineTextAlignment(.center) + .accessibilityAddTraits(.isHeader) + .accessibilityElement(children: .combine) + .accessibilityHint("Choose one diet from the next list") + } + } + .listStyle(.plain) + .toolbar { + ToolbarItem(placement: .bottomBar) { + Button( + action: { model.displayServingsCounter() }, + label: { + Text("Continue".uppercased()) + .bold() + .frame(maxWidth: .infinity) + } + ) + .disabled(preferences.diet == nil) + .buttonStyle(.borderedProminent) + .controlSize(.large) + .padding() + } + } + .toolbar { + ToolbarItem { + Button("Skip") { model.displayServingsCounter() } + } + } + } +} + +#Preview { + NavigationStack { + DietSelector() + .environment(OnboardingFeatureModel()) + .environment(Preferences()) + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/GoalsSelector.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/GoalsSelector.swift new file mode 100644 index 0000000..29f28a9 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/GoalsSelector.swift @@ -0,0 +1,87 @@ +import SwiftUI + +struct GoalsSelector: View { + + @Environment(OnboardingFeatureModel.self) + var model + + @Environment(Preferences.self) + var preferences + + var body: some View { + List { + Section { + ForEach(Goal.allCases) { goal in + let isSelected = preferences.goals.contains(goal) + Button( + action: { + if isSelected { + preferences.goals.remove(goal) + } else { + preferences.goals.insert(goal) + } + }, + label: { + Label(goal.rawValue, systemImage: goal.systemImage) + .accessibilityLabel(goal.rawValue + (isSelected ? ". selected" : "")) + } + ) + .tag(goal) + .listItemTint(isSelected ? Color.primary : .accentColor) + .listRowBackground(isSelected ? Color.accentColor : .clear ) + .accessibilityAction(named: "Continue") { + model.displayDietSelector() + } + } + } header: { + VStack { + Spacer() + .frame(width: .zero, height: 0) + .accessibilityLabel("Step 1 of 4") + + Text("What brings you to Hello Würst?") + .font(.system(.title, design: .serif)) + .bold() + + Text("We base our recipe recommendations on your goals.") + .fontWeight(.regular) + } + .foregroundStyle(.primary) + .padding(.bottom, 30) + .multilineTextAlignment(.center) + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.isHeader) + .accessibilityHint("You can choose multiple goals from the list") + } + } + .listStyle(.plain) + .toolbar { + ToolbarItem(placement: .bottomBar) { + Button( + action: { model.displayDietSelector() }, + label: { + Text("Continue".uppercased()) + .frame(maxWidth: .infinity) + } + ) + .disabled(preferences.goals.isEmpty) + .buttonStyle(.borderedProminent) + .controlSize(.large) + .padding() + } + } + .toolbar { + ToolbarItem { + Button("Skip") { model.displayDietSelector() } + } + } + } +} + +#Preview { + NavigationStack { + GoalsSelector() + .environment(OnboardingFeatureModel()) + .environment(Preferences()) + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingFeatureModel.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingFeatureModel.swift new file mode 100644 index 0000000..00f20c0 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingFeatureModel.swift @@ -0,0 +1,46 @@ +import Foundation +import SwiftUI + +@Observable +class OnboardingFeatureModel { + + enum Destination { + case goals + case diet + case servings + case almost + } + + var navigationPath: [Destination] = [] + var onboardingDidComplete: () -> Void = {} + var currentSlide = 0 + private(set) var isReady = false + + func displayGoalsSelector() { + navigationPath.append(.goals) + } + + func displayDietSelector() { + navigationPath.append(.diet) + } + + func displayServingsCounter() { + navigationPath.append(.servings) + } + + func displayAlmostThere() { + navigationPath.append(.almost) + } + + func nextSlide() { + if currentSlide >= 2 { + currentSlide = 0 + } else { + currentSlide += 1 + } + } + + func onboardingIsReady() { + isReady = true + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingStepView.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingStepView.swift new file mode 100644 index 0000000..8b86a36 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingStepView.swift @@ -0,0 +1,47 @@ +import SwiftUI + +struct OnboardingStepView: View { + let title: String + let subtitle: String + let imageName: String + + var body: some View { + ZStack { + Color.clear + .background { + // This image is created with an explicit (accessibility) label. + // TODO: create image description + Image(decorative: imageName) + .resizable() + .aspectRatio(contentMode: .fill) + .offset(y: -12) + } + + VStack { + Text(title) + .font(.system(.title, design: .serif)) + .bold() + + Text(subtitle) + + Spacer() + } + .accessibilityElement(children: .combine) + .foregroundColor(.white) + .padding(.vertical, 32) + .multilineTextAlignment(.center) + + + } + } +} + + + +#Preview { + OnboardingStepView( + title: "Hello Würst! Quick & healthy recipes incoming!", + subtitle: "Discover new recipes everyday — for free!", + imageName: "agnieszka-kowalczyk-obMdrL5pFWI-unsplash" + ) +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingView.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingView.swift new file mode 100644 index 0000000..803ff1c --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/OnboardingView.swift @@ -0,0 +1,37 @@ +import SwiftUI + +struct OnboardingView: View { + @Environment(OnboardingFeatureModel.self) private var model + + var body: some View { + @Bindable var model = model + + NavigationStack(path: $model.navigationPath) { + PresentationView() + .background(Color.black) + .ignoresSafeArea() + .navigationDestination(for: OnboardingFeatureModel.Destination.self) { destination in + switch destination { + case .goals: + GoalsSelector() + + case .diet: + DietSelector() + + case .servings: + ServingsCounter() + + case .almost: + AlmostThere() + } + } + } + } +} + +#Preview { + OnboardingView() + .environment(OnboardingFeatureModel()) + .environment(Preferences()) +} + diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/PresentationView.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/PresentationView.swift new file mode 100644 index 0000000..875045a --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/PresentationView.swift @@ -0,0 +1,113 @@ +import SwiftUI + +struct PresentationView: View { + + @Environment(OnboardingFeatureModel.self) + var model + + @Environment(\.accessibilityVoiceOverEnabled) + var voiceOverEnabled: Bool + + private let timer = Timer.publish(every: 5, tolerance: 0.5, on: .main, in: .common).autoconnect() + + private func cancelTimer() { + timer.upstream.connect().cancel() + } + + var body: some View { + @Bindable var model = model + let swipeToStopTimer = DragGesture() + .onChanged { _ in + cancelTimer() + } + + TabView(selection: $model.currentSlide) { + OnboardingStepView( + title: "Hello Würst! Quick & healthy recipes incoming!", + subtitle: "Discover new recipes everyday — for free!", + imageName: "agnieszka-kowalczyk-obMdrL5pFWI-unsplash" + ) + .tabItem { Text("Hello") } + .tag(0) + + OnboardingStepView( + title: "Get personalized recipes", + subtitle: "Choose between 9 different diets & leave out ingredients you don't eat.", + imageName: "benjamin-kaufmann-sb3Cv_K14Js-unsplash" + ) + .tabItem { Text("Personalized") } + .tag(1) + + OnboardingStepView( + title: "Find recipes based on ingredients", + subtitle: "Use up leftover ingredients with our `Fridge Finds` feature", + imageName: "ratul-ghosh-NPrWYa69Mz0-unsplash" + ) + .tabItem { Text("Find") } + .tag(2) + } + .accessibilityRepresentation { + Text( + """ + Hello Würst! Quick & healthy recipes incoming! + Discover new recipes everyday — for free! + + Get personalized recipes + Choose between 9 different diets & leave out ingredients you don't eat. + + Find recipes based on ingredients + Use up leftover ingredients with our `Fridge Finds` feature + """ + ) + .accessibilityAddTraits(.isHeader) + .accessibilitySortPriority(1) + } + .tabViewStyle(.page(indexDisplayMode: .never)) + .animation(.default, value: model.currentSlide) + .overlay(alignment: .bottom) { + VStack { + Button( + action: { model.displayGoalsSelector() }, + label: { + Text("Let's get started".uppercased()) + .bold() + .frame(maxWidth: .infinity) + } + ) + .buttonStyle(.borderedProminent) + .controlSize(.extraLarge) + .accessibilitySortPriority(0) + + Text( + """ + By continuing, I agree to Hello Würst's [Terms of Use](https://www.youtube.com/watch?v=dQw4w9WgXcQ) + and [Privacy Policy](https://www.youtube.com/watch?v=CduA0TULnow) + """ + ) + .font(.footnote) + .multilineTextAlignment(.center) + } + .foregroundColor(.white) + .padding(.horizontal) +#if os(visionOS) + .padding(.bottom) +#elseif os(iOS) + .padding(.bottom, 60) +#endif + } + .onReceive(timer) { _ in + model.nextSlide() + } + .gesture(swipeToStopTimer) + .task { + if voiceOverEnabled { + cancelTimer() + } + } + } +} + +#Preview { + PresentationView() + .environment(OnboardingFeatureModel()) +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/ServingsCounter.swift b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/ServingsCounter.swift new file mode 100644 index 0000000..f329a61 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/OnboardingFeature/ServingsCounter.swift @@ -0,0 +1,65 @@ +import SwiftUI + +struct ServingsCounter: View { + + @Environment(OnboardingFeatureModel.self) + var model + + var body: some View { + List { + Section { + VStack { + Spacer() + ServingsPicker(size: 50) + } + .padding(.horizontal) + .listRowSeparator(.hidden) + } header: { + VStack { + Spacer() + .frame(width: .zero, height: 0) + .accessibilityLabel("Step 3 of 4") + + Text("How many servings do you usually cook?") + .font(.system(.title, design: .serif)) + .bold() + + Text("You can change your serving count at any time.") + .fontWeight(.regular) + } + .frame(maxWidth: .infinity) + .foregroundStyle(.primary) + .padding(.bottom, 30) + .multilineTextAlignment(.center) + .accessibilityAddTraits(.isHeader) + .accessibilityElement(children: .combine) + } + } + .listStyle(.plain) + .toolbar { + ToolbarItem(placement: .bottomBar) { + Button( + action: { model.displayAlmostThere() }, + label: { + Text("Continue".uppercased()) + .frame(maxWidth: .infinity) + } + ) + .buttonStyle(.borderedProminent) + .controlSize(.large) + .padding() + } + } + } +} + +#Preview { + NavigationStack { + ServingsCounter() + .environment(OnboardingFeatureModel()) + .environment(Preferences()) + } +} + + + diff --git a/Examples/visionOS/HelloWurst/HelloWurst/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/visionOS/HelloWurst/HelloWurst/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Diet.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Diet.swift new file mode 100644 index 0000000..0689e0f --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Diet.swift @@ -0,0 +1,65 @@ +import Foundation + +enum Diet: String, CaseIterable, Identifiable { + var id: Self { self } + + case everything = "I eat everything" + case vegetarian = "Vegetarian" + case vegan = "Vegan" + case lowCarb = "Low carb" + case mediterranean = "Mediterranean" + case pescetarian = "Pescetarian" + case paleo = "Paleo" + case keto = "Keto" + case highProtein = "High protein" +} + +extension Diet { + var systemImage: String { + switch self { + case .everything: + "hare" + case .vegetarian: + "leaf" + case .vegan: + "laurel.trailing" + case .lowCarb: + "moon.circle" + case .mediterranean: + "sun.horizon" + case .pescetarian: + "fish" + case .paleo: + "fossil.shell" + case .keto: + "drop" + case .highProtein: + "atom" + } + } +} + +extension Diet { + var isIncludedInFreeVersion: Bool { + switch self { + case .everything: + true + case .vegetarian: + true + case .vegan: + false + case .lowCarb: + false + case .mediterranean: + false + case .pescetarian: + false + case .paleo: + false + case .keto: + false + case .highProtein: + false + } + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Goal.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Goal.swift new file mode 100644 index 0000000..ece601f --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Goal.swift @@ -0,0 +1,37 @@ +import Foundation + +enum Goal: String, CaseIterable, Identifiable { + var id: Self { self } + + case inspiration = "Find recipe inspiration" + case health = "Eat healthier" + case learn = "Learn how to cook" + case diet = "Follow a certain diet" + case plan = "Plan my meals" + case time = "Save time" + case money = "Save money" + case other = "Other" +} + +extension Goal { + var systemImage: String { + switch self { + case .inspiration: + "lightbulb.led" + case .health: + "carrot" + case .learn: + "graduationcap" + case .diet: + "frying.pan" + case .plan: + "calendar" + case .time: + "clock" + case .money: + "banknote" + case .other: + "laurel.leading" + } + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Preferences.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Preferences.swift new file mode 100644 index 0000000..70d2dbd --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Preferences.swift @@ -0,0 +1,9 @@ +import Foundation +import SwiftUI + +@Observable +class Preferences { + var goals: Set = [] + var diet: Diet? + var servings: UInt = 2 +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Recipe+Samples.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Recipe+Samples.swift new file mode 100644 index 0000000..13d597b --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Recipe+Samples.swift @@ -0,0 +1,199 @@ +import Foundation + +extension Recipe { + static var samples = [ + Recipe( + name: "Die einzig wahre Currywurst", + diet: .everything, + image: "classic-truck", + time: .seconds(15 * 60), + price: 7, + authors: [ + Author( + name: "Eugénie Brazie", + description: "", + source: URL(string: "https://en.wikipedia.org/wiki/Eugénie_Brazier")! + ) + ], + required: [ + Ingredient(amount: .mass(.init(value: 250, unit: .grams)), name: "Potatoes"), + Ingredient(amount: .mass(.init(value: 75, unit: .grams)), name: "Tomato paste"), + ], + mightHave: [ + Ingredient(amount: .volume(.init(value: 250, unit: .milliliters)), name: "Water") + ], + nutritionFacts: [ + NutritionFact(factType: .calorie(.init(value: 707, unit: .calories))), + NutritionFact(factType: .protein(.init(value: 37, unit: .grams))), + NutritionFact(factType: .fat(.init(value: 28, unit: .grams))), + NutritionFact(factType: .carbohydrate(.init(value: 77, unit: .grams))), + ], + steps: [ + .init(name: "1"), + .init(name: "2"), + .init(name: "3", time: .init(.seconds(60 * 3))), + .init(name: "4"), + .init(name: "5", time: .init(.seconds(60))), + .init(name: "6"), + .init(name: "7"), + ] + ), + Recipe( + name: "Gingery orange", + diet: .vegan, + image: "gingery-orange", + time: .seconds(7 * 60), + price: 13, + authors: [ + Author( + name: "Elena Arzak", + description: "", + source: URL(string: "https://en.wikipedia.org/wiki/Elena_Arzak")! + ) + ], + required: [ + Ingredient(amount: .mass(.init(value: 250, unit: .grams)), name: "Potatoes"), + Ingredient(amount: .mass(.init(value: 10, unit: .grams)), name: "Ginger"), + ], + mightHave: [ + Ingredient(amount: .volume(.init(value: 250, unit: .milliliters)), name: "Water"), + Ingredient(amount: .subjective("a pinch of"), name: "Salt"), + ], + nutritionFacts: [ + NutritionFact(factType: .calorie(.init(value: 707, unit: .calories))), + NutritionFact(factType: .protein(.init(value: 37, unit: .grams))), + NutritionFact(factType: .fat(.init(value: 28, unit: .grams))), + NutritionFact(factType: .carbohydrate(.init(value: 77, unit: .grams))), + ], + steps: [ + .init(name: "1"), + .init(name: "2"), + .init(name: "3"), + .init(name: "4"), + .init(name: "5"), + .init(name: "6"), + .init(name: "7"), + .init(name: "8"), + .init(name: "9"), + .init(name: "10"), + .init(name: "11"), + ] + ), + Recipe( + name: "Green coconut", + diet: .vegetarian, + image: "green-coconut", + time: .seconds(23 * 60), + price: 15, + authors: [ + Author( + name: "Marie Bourgeois", + description: "", + source: URL(string: "https://en.wikipedia.org/wiki/Marie_Bourgeois")! + ) + ], + required: [ + Ingredient(amount: .mass(.init(value: 250, unit: .grams)), name: "Potatoes"), + Ingredient(amount: .mass(.init(value: 10, unit: .grams)), name: "Cilantro fresh"), + + ], + mightHave: [ + Ingredient(amount: .volume(.init(value: 250, unit: .milliliters)), name: "Water") + ], + nutritionFacts: [ + NutritionFact(factType: .calorie(.init(value: 707, unit: .calories))), + NutritionFact(factType: .protein(.init(value: 37, unit: .grams))), + NutritionFact(factType: .fat(.init(value: 28, unit: .grams))), + NutritionFact(factType: .carbohydrate(.init(value: 77, unit: .grams))), + ], + steps: [ + .init(name: "1"), + .init(name: "2"), + .init(name: "3"), + .init(name: "4"), + .init(name: "5"), + .init(name: "6"), + .init(name: "7"), + .init(name: "8"), + .init(name: "9"), + ] + ), + Recipe( + name: "Honey mustard", + diet: .mediterranean, + image: "honey-mustard", + time: .seconds(7 * 60), + price: 9, + authors: [ + Author( + name: "Marguerite Bise", + description: "", + source: URL(string: "https://en.wikipedia.org/wiki/Marguerite_Bise")! + ) + ], + required: [ + Ingredient(amount: .mass(.init(value: 250, unit: .grams)), name: "Potatoes"), + Ingredient(amount: .subjective("◐"), name: "Yellow onion"), + Ingredient(amount: .mass(.init(value: 160, unit: .grams)), name: "Ground beef"), + ], + mightHave: [ + Ingredient(amount: .volume(.init(value: 250, unit: .milliliters)), name: "Water") + ], + nutritionFacts: [ + NutritionFact(factType: .calorie(.init(value: 707, unit: .calories))), + NutritionFact(factType: .protein(.init(value: 37, unit: .grams))), + NutritionFact(factType: .fat(.init(value: 28, unit: .grams))), + NutritionFact(factType: .carbohydrate(.init(value: 77, unit: .grams))), + ], + steps: [ + .init(name: "1"), + .init(name: "2"), + .init(name: "3", time: .init(.seconds(60 * 5))), + .init(name: "4"), + .init(name: "5"), + .init(name: "6"), + .init(name: "7"), + .init(name: "8"), + .init(name: "9"), + .init(name: "10"), + .init(name: "11"), + .init(name: "12"), + .init(name: "13"), + ] + ), + Recipe( + name: "Radish arugula", + diet: .everything, + image: "radish-arugula", + time: .seconds(9 * 60), + price: 11, + authors: [ + Author( + name: "Sophie Bise", + description: "", + source: URL(string: "https://en.wikipedia.org/wiki/Sophie_Bise")! + ) + ], + required: [ + Ingredient(amount: .mass(.init(value: 250, unit: .grams)), name: "Potatoes"), + Ingredient(amount: .mass(.init(value: 100, unit: .grams)), name: "greeb peas, frozen"), + ], + mightHave: [ + Ingredient(amount: .volume(.init(value: 250, unit: .milliliters)), name: "Water") + ], + nutritionFacts: [ + NutritionFact(factType: .calorie(.init(value: 707, unit: .calories))), + NutritionFact(factType: .protein(.init(value: 37, unit: .grams))), + NutritionFact(factType: .fat(.init(value: 28, unit: .grams))), + NutritionFact(factType: .carbohydrate(.init(value: 77, unit: .grams))), + ], + steps: [ + .init(name: "1"), + .init(name: "2"), + .init(name: "3"), + .init(name: "4"), + .init(name: "5"), + ] + ), + ] +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Recipe.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Recipe.swift new file mode 100644 index 0000000..c5b7b02 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedModels/Recipe.swift @@ -0,0 +1,82 @@ +import Foundation + +struct Recipe: Identifiable { + var id = UUID() + + let name: String + let diet: Diet + let image: String + let time: Duration + let price: Double + + let authors: [Author] + let required: [Ingredient] + let mightHave: [Ingredient] + let nutritionFacts: [NutritionFact] + let steps: [Step] +} + +extension Recipe { + struct Author: Identifiable { + var id = UUID() + + let name: String + let description: String? + let source: URL + } +} + +extension Recipe { + enum Amount { + case mass(Measurement) + case subjective(String) + case volume(Measurement) + } + struct Ingredient: Identifiable { + var id = UUID() + + let amount: Amount + let name: String + } +} + +extension Recipe.Amount { + func formatted(servings: UInt) -> String { + switch self { + case .mass(let amount): + let amountForServings = Measurement(value: amount.value * Double(servings), unit: amount.unit) + return amountForServings.formatted() + + case .subjective(let amount): + return amount + + case .volume(let amount): + let amountForServings = Measurement(value: amount.value * Double(servings), unit: amount.unit) + return amountForServings.formatted() + } + } +} + +extension Recipe { + enum NutritionFactType { + case calorie(Measurement) + case protein(Measurement) + case fat(Measurement) + case carbohydrate(Measurement) + } + + struct NutritionFact: Identifiable { + var id = UUID() + + let factType: NutritionFactType + } +} + +extension Recipe { + struct Step: Identifiable, Hashable { + var id = UUID() + + let name: String + var time: Duration? = nil + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedViews/RecipeView.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedViews/RecipeView.swift new file mode 100644 index 0000000..3376389 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedViews/RecipeView.swift @@ -0,0 +1,298 @@ +import SwiftUI + +struct RecipeView: View { + + @Environment(AppCoreFeatureModel.self) + var model + + let recipe: Recipe + + var body: some View { + @Bindable var model = model + + Grid { +#if os(visionOS) + GridRow { + List { + RecipeSections(recipe: recipe) + } + Image(decorative: recipe.image) + .resizable() + .aspectRatio(contentMode: .fill) + .containerRelativeFrame(.vertical) + } + .ornament( + attachmentAnchor: .scene(.topTrailing), + contentAlignment: .trailing, + ornament: { + HStack { + Button("Love", systemImage: "heart", action: {}) + .buttonStyle(.borderless) + .labelStyle(.iconOnly) + + Button("Notes", systemImage: "note.text", action: {}) + .buttonStyle(.borderless) + .labelStyle(.iconOnly) + + Button("Share", systemImage: "square.and.arrow.up", action: {}) + .buttonStyle(.borderless) + .labelStyle(.iconOnly) + } + .padding(8) + .glassBackgroundEffect() + .padding(.trailing, 60) + } + ) +#elseif os(iOS) + List { + Section { + Color.clear + .aspectRatio(contentMode: .fit) + .background { + Image(decorative: recipe.image) + .resizable() + .aspectRatio(contentMode: .fill) + /// > Tip: inner corner radius + padding = outer corner radius + .mask(RoundedRectangle(cornerRadius: 8).aspectRatio(contentMode: .fit)) + } + } + + RecipeSections(recipe: recipe) + } + .toolbar { + ToolbarItem { + HStack { + Button("Love", systemImage: "heart", action: {}) + Button("Notes", systemImage: "note.text", action: {}) + Button("Share", systemImage: "square.and.arrow.up", action: {}) + } + } + } +#endif + } + .navigationTitle(recipe.name) + .toolbar { + ToolbarItem(placement: .bottomBar) { + Button( + action: { model.displayStepByStep(recipe: recipe) }, + label: { + Text("Preparation".uppercased()) + .frame(maxWidth: .infinity) + } + ) + .buttonStyle(.borderedProminent) + .controlSize(.large) + } + } + .sheet( + isPresented: $model.isStepByStepPresented, + onDismiss: { model.hideStepByStep() } + ) { + NavigationStack { + StepByStepView() + .environment(StepByStepFeatureModel(steps: recipe.steps, didComplete: { model.hideStepByStep() } )) + .toolbar { +#if os(visionOS) + ToolbarItem(placement: .navigation) { + /// Design for spatial user interfaces + /// https://developer.apple.com/videos/play/wwdc2023-10076 + Button( + "Close", + systemImage: "xmark", + action: { model.hideStepByStep() } + ) + } +#elseif os(iOS) + ToolbarItem(placement: .automatic) { + Button( + "Close", + systemImage: "xmark.circle.fill", + action: { model.hideStepByStep() } + ) + } +#endif + } + } + } + } +} + +struct RecipeSections: View { + let recipe: Recipe + + var body: some View { + Section("Recipe by:".uppercased()) { + AuthorView(recipe: recipe) + } + + Section("Servings".uppercased()) { + ServingsPicker(size: 50) + } + + Section("You need".uppercased()) { + IngredientsView(ingredients: recipe.required) + .fontDesign(.serif) + } + + Section("You might have this at home".uppercased()) { + IngredientsView(ingredients: recipe.mightHave) + .fontDesign(.serif) + } + + Section( + content: { + NutritionFactsView(recipe: recipe) + .fontDesign(.serif) + }, + header: { + HStack { + Text("Nutrition facts".uppercased()) + Spacer() + Text("per serving (approx.)").font(.caption) + } + .accessibilityElement(children: .combine) + } + ) + } +} + +// MARK: - Nutrition Facts View + +struct NutritionFactsView: View { + let recipe: Recipe + + var items: [GridItem] = [ + .init(.adaptive(minimum: 100)) + ] + + var body: some View { + LazyVGrid(columns: items) { + ForEach(recipe.nutritionFacts) { fact in + switch fact.factType { + case .calorie(let fact): + VStack { + Text(fact.value, format: .number).font(.title2) + Text("Cal").font(.caption).foregroundColor(.secondary) + } + .fixedSize() + .padding(24) + .background(Circle().stroke().fill(.yellow)) + .accessibilityElement(children: .combine) + + case .protein(let fact): + VStack { + Text(fact.formatted()).font(.title2) + Text("Protein").font(.caption).foregroundColor(.secondary) + } + .fixedSize() + .padding(24) + .background(Circle().stroke().fill(.green)) + .accessibilityElement(children: .combine) + + case .fat(let fact): + VStack { + Text(fact.formatted()).font(.title2) + Text("Fat").font(.caption).foregroundColor(.secondary) + } + .fixedSize() + .padding(24) + .background(Circle().stroke().fill(.orange)) + .accessibilityElement(children: .combine) + + case .carbohydrate(let fact): + VStack { + Text(fact.formatted()).font(.title2) + Text("Carb").font(.caption).foregroundColor(.secondary) + } + .fixedSize() + .padding(24) + .background(Circle().stroke().fill(.gray)) + .accessibilityElement(children: .combine) + } + } + } + } +} + +// MARK: - Author View + +struct AuthorView: View { + @Environment(\.openURL) private var openURL + let recipe: Recipe + + var body: some View { + ForEach(recipe.authors) { author in + HStack { + Text(author.name) + + Spacer() + + Button("More info") { openURL(author.source) } + .buttonStyle(.borderedProminent) + } + .accessibilityElement(children: .combine) + .fontDesign(.serif) + } + } +} + +// MARK: - Ingredients View + +struct IngredientsView: View { + @Environment(Preferences.self) private var preferences + let ingredients: [Recipe.Ingredient] + + var body: some View { + ForEach(ingredients) { ingredient in + LabeledContent( + ingredient.name, + value: ingredient.amount.formatted(servings: preferences.servings) + ) + } + } +} + +// MARK: - Preview + +#Preview { + NavigationStack { + RecipeView(recipe: Recipe.samples[0]) + } + .environment(AppCoreFeatureModel()) + .environment(Preferences()) +} + +#Preview { + NavigationStack { + RecipeView(recipe: Recipe.samples[1]) + } + .environment(AppCoreFeatureModel()) + .environment(Preferences()) +} + +#Preview { + NavigationStack { + RecipeView(recipe: Recipe.samples[2]) + } + .environment(AppCoreFeatureModel()) + .environment(Preferences()) +} + +#Preview { + NavigationView { + RecipeView(recipe: Recipe.samples[3]) + } + .environment(AppCoreFeatureModel()) + .environment(Preferences()) +} + +#Preview { + NavigationStack { + List { + Section { + NutritionFactsView(recipe: Recipe.samples[3]) + } + } + } + .environment(Preferences()) +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/SharedViews/ServingsPicker.swift b/Examples/visionOS/HelloWurst/HelloWurst/SharedViews/ServingsPicker.swift new file mode 100644 index 0000000..135bfee --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/SharedViews/ServingsPicker.swift @@ -0,0 +1,70 @@ +import SwiftUI + +struct ServingsPicker: View { + + @Environment(Preferences.self) + var preferences + + var size: CGFloat = 100 + + var body: some View { + @Bindable + var preferences = preferences + + let shape = RoundedRectangle(cornerRadius: size / 3) + + HStack { + Button( + "Less", + systemImage: "minus", + action: { preferences.servings -= 1 } + ) + .controlSize(.large) + .buttonStyle(.bordered) + .buttonBorderShape(.circle) + .labelStyle(.iconOnly) + .disabled(preferences.servings == 0) + + Spacer() + + Text(preferences.servings, format: .number) + .font(.system(size: size, weight: .medium, design: .serif)) + .padding(.horizontal) + + Spacer() + + Button( + "More", + systemImage: "plus", + action: { preferences.servings += 1 } + ) + .controlSize(.large) + .buttonStyle(.bordered) + .buttonBorderShape(.circle) + .labelStyle(.iconOnly) + .disabled(preferences.servings == 12) + } +#if os(visionOS) + .padding() + .background() + .clipShape(shape) +#elseif os(iOS) + .padding(8) + .background { shape.fill(Color.primary).colorInvert() } + .overlay { shape.stroke(.secondary) } +#endif + .accessibilityRepresentation { + Stepper(value: $preferences.servings, in: 1...12) { + Text("Servings".uppercased()) + } + } + } +} + +#Preview { + NavigationStack { + ServingsPicker() + .environment(Preferences()) + .padding() + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/ClockView.swift b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/ClockView.swift new file mode 100644 index 0000000..0d51540 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/ClockView.swift @@ -0,0 +1,17 @@ +import SwiftUI +import RealityKit +import RealityCheckConnect + +//#if os(visionOS) +struct ClockView: View { + var body: some View { + Model3D(named: "Wall_Kitchen_Clock_50s") + //.rotation3DEffect(.degrees(-90), axis: .y) + } +} + +#Preview { + ClockView() +} +//#endif + diff --git a/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepByStepFeatureModel.swift b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepByStepFeatureModel.swift new file mode 100644 index 0000000..6bad92b --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepByStepFeatureModel.swift @@ -0,0 +1,18 @@ +import Foundation +import SwiftUI + +@Observable +class StepByStepFeatureModel { + var steps: [Recipe.Step] + var current: Recipe.Step + var didComplete: () -> Void + + init( + steps: [Recipe.Step], + didComplete: @escaping () -> Void = {} + ) { + self.steps = steps + self.current = steps[0] + self.didComplete = didComplete + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepByStepView.swift b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepByStepView.swift new file mode 100644 index 0000000..995dd70 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepByStepView.swift @@ -0,0 +1,42 @@ +import SwiftUI + +struct StepByStepView: View { + @Environment(StepByStepFeatureModel.self) private var model + + var body: some View { + @Bindable var model = model + + TabView { + ForEach(model.steps) { step in + VStack { + StepView(step: step) + + if step == model.steps.last { + Button( + "Mark as cooked".uppercased(), + systemImage: "checkmark", + action: { model.didComplete() } + ) + .controlSize(.extraLarge) +#if os(iOS) + .buttonStyle(.borderedProminent) +#endif + } + } + .tag(step) + } + } + .tabViewStyle(.page) + .indexViewStyle(.page(backgroundDisplayMode: .always)) + .onChange(of: model.current) { oldValue, newValue in + print(oldValue, newValue) + } + } +} + +#Preview { + NavigationStack { + StepByStepView() + .environment(StepByStepFeatureModel(steps: Recipe.samples[0].steps)) + } +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepView.swift b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepView.swift new file mode 100644 index 0000000..d273543 --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/StepView.swift @@ -0,0 +1,29 @@ +import SwiftUI + +struct StepView: View { + let step: Recipe.Step + + @Environment(\.openWindow) + var openWindow + + var body: some View { + VStack { + Text("Step: " + step.name) + .font(.largeTitle) + .fontDesign(.serif) + + if let time = step.time { + Button( + time.formatted(), + systemImage: "clock.badge", + action: { openWindow(id: "Clock") } + ) + .buttonStyle(.bordered) + } + } + } +} + +#Preview { + StepView(step: .init(name: "Hello, Step!", time: .init(.seconds(60 * 5)))) +} diff --git a/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/Wall_Kitchen_Clock_50s.usdz b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/Wall_Kitchen_Clock_50s.usdz new file mode 100644 index 0000000..5eb1b84 Binary files /dev/null and b/Examples/visionOS/HelloWurst/HelloWurst/StepByStepFeature/Wall_Kitchen_Clock_50s.usdz differ diff --git a/Examples/visionOS/HelloWurst/HelloWurst/TodayFeature/TodayView.swift b/Examples/visionOS/HelloWurst/HelloWurst/TodayFeature/TodayView.swift new file mode 100644 index 0000000..0fa3fdc --- /dev/null +++ b/Examples/visionOS/HelloWurst/HelloWurst/TodayFeature/TodayView.swift @@ -0,0 +1,144 @@ +import SwiftUI + +struct TodayView: View { + + @Environment(AppCoreFeatureModel.self) + var model + + @State + var scrolledRecipe: Recipe.ID? + + @State + var displayOrnament = false + + var body: some View { + NavigationStack { + ScrollView { + LazyVStack(spacing: 0.0) { + ForEach(model.recipes) { recipe in + NavigationLink(destination: RecipeView(recipe: recipe)) { + VStack { + Spacer() +#if os(visionOS) + Color.clear +#elseif os(iOS) + Summary(recipe: recipe) + .padding(.horizontal) + .background(.black.opacity(0.5)) + .padding(.bottom, 80) + .accessibilityHidden(true) +#endif + } + .background( + Image(decorative: recipe.image) + .resizable() + .scaledToFill() + ) + .clipped() + .containerRelativeFrame(.horizontal) + .containerRelativeFrame(.vertical) + .accessibilityRepresentation { + Summary(recipe: recipe) + } + .accessibilityAction(named: "Love") { print("Love") } + .accessibilityAction(named: "Notes") { print("Notes") } + .accessibilityAction(named: "Share") { print("Share") } + .hoverEffect() + } + } + } + .scrollTargetLayout() + } + .scrollTargetBehavior(.paging) + .scrollPosition(id: $scrolledRecipe) + .ignoresSafeArea() +#if os(visionOS) + .ornament( + visibility: model.isOnboadingPresented ? .hidden : .visible, + attachmentAnchor: .scene(.bottom) + ) { + if let recipe = model.recipes.first(where: { $0.id == scrolledRecipe }) { + Summary(recipe: recipe) + .padding() + .glassBackgroundEffect() + .accessibilityHidden(true) + } + } +#endif + .task { + scrolledRecipe = model.recipes.first?.id + displayOrnament = true + } + } + } +} + +struct Summary: View { + + @Environment(Preferences.self) + var preferences + + @Environment(\.locale) + var locale + + let recipe: Recipe + + var body: some View { + let price = (recipe.price * Double(preferences.servings)) + .formatted(.currency(code: locale.currency?.identifier ?? "EUR")) + + VStack(alignment: .leading) { + Text(recipe.name) + .font(.system(.largeTitle, design: .serif, weight: .bold)) + + Spacer().frame(maxHeight: 60) + + HStack(alignment: .bottom) { + let cookingTime = recipe.time.formatted(.units(allowed: [.minutes], width: .abbreviated)) + Label(cookingTime, systemImage: "timer") + .accessibilityLabel("Cooking time \(cookingTime)") + + Spacer() + + Image(systemName: recipe.diet.systemImage) + .font(.title) + .opacity(0.5) + .accessibilityLabel("Diet: \(recipe.diet.rawValue)") + .accessibilitySortPriority(-1) + + Divider().overlay(.white).frame(maxHeight: 55) + + VStack(alignment: .leading) { + Label( + preferences.servings.formatted() + " Pers", + systemImage: "person.2" + ) + .font(.caption) + .accessibilityHidden(true) +#if os(visionOS) + .foregroundColor(.primary) + .padding(4) + .background(Color.accentColor) + .cornerRadius(4.0) +#elseif os(iOS) + .foregroundColor(.accentColor) + .background(.clear) +#endif + Text(price) + .font(.title) + .accessibilityLabel("Estimated cost \(price) for \(preferences.servings) persons") + } + } + } + .padding() + .foregroundColor(.white) + } +} + +#Preview { + NavigationStack { + TodayView() + .environment(AppCoreFeatureModel(recipes: Recipe.samples)) + .environment(Preferences()) + } +} diff --git a/Examples/visionOS/Package.swift b/Examples/visionOS/Package.swift new file mode 100644 index 0000000..a6ee1f2 --- /dev/null +++ b/Examples/visionOS/Package.swift @@ -0,0 +1,11 @@ +// swift-tools-version:5.2 + +// Leave blank. This is only here so that Xcode doesn't display it. + +import PackageDescription + +let package = Package( + name: "visionos-examples", + products: [], + targets: [] +) diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json new file mode 100644 index 0000000..35c2d94 --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json @@ -0,0 +1,11 @@ +{ + "pathsToIds" : { + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "CB766F92-EE55-4A63-9401-E7B8C009764D", + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "65F6F990-A780-4474-B78B-572E0E4E273D", + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "0A9B4653-B11E-4D6A-850E-C6FCB621626C", + "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Untitled Scene.usda" : "D560BB77-AAF3-4BDE-B7C4-989332A4688B", + "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "66168B71-AB05-424E-8B6C-D33D6E61B08F", + "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "AF09ED6F-1707-48FD-8720-65B998362C09", + "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "D66134B1-3681-4A8E-AFE5-29F257229F3B" + } +} \ No newline at end of file diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json new file mode 100644 index 0000000..f3d44aa --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json @@ -0,0 +1,242 @@ +{ + "0A9B4653-B11E-4D6A-850E-C6FCB621626C" : { + "cameraTransform" : [ + 1, + 0, + 0, + 0, + 0, + 0.86602545, + -0.49999994, + 0, + 0, + 0.49999994, + 0.86602545, + 0, + 0.0035969093, + 0.35542378, + 0.62919164, + 1 + ], + "objectMetadataList" : [ + [ + "0A9B4653-B11E-4D6A-850E-C6FCB621626C", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "65F6F990-A780-4474-B78B-572E0E4E273D" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.86602545, + -0.49999988, + 0, + 0, + 0.49999988, + 0.86602545, + 0, + 1.1972517e-08, + 2.6179132, + 0.43191218, + 1 + ], + "objectMetadataList" : [ + [ + "65F6F990-A780-4474-B78B-572E0E4E273D", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "66168B71-AB05-424E-8B6C-D33D6E61B08F" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.8660254, + -0.5, + 0, + 0, + 0.5, + 0.8660254, + 0, + 0, + 0.23875366, + 0.4135335, + 1 + ], + "objectMetadataList" : [ + [ + "66168B71-AB05-424E-8B6C-D33D6E61B08F", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "AF09ED6F-1707-48FD-8720-65B998362C09" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.7071069, + -0.7071067, + 0, + 0, + 0.7071067, + 0.7071069, + 0, + 0, + 2.8836339, + -0.107588194, + 1 + ], + "objectMetadataList" : [ + [ + "AF09ED6F-1707-48FD-8720-65B998362C09", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "AF09ED6F-1707-48FD-8720-65B998362C09", + "Root", + "Sphere_Left" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "AF09ED6F-1707-48FD-8720-65B998362C09", + "Root", + "Sphere_Right" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "CB766F92-EE55-4A63-9401-E7B8C009764D" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.8660253, + -0.5000001, + 0, + 0, + 0.5000001, + 0.8660253, + 0, + 0, + 0.27093494, + 0.4692731, + 1 + ], + "objectMetadataList" : [ + [ + "CB766F92-EE55-4A63-9401-E7B8C009764D", + "Root", + "GridMaterial" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "CB766F92-EE55-4A63-9401-E7B8C009764D", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + }, + "D560BB77-AAF3-4BDE-B7C4-989332A4688B" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.8660253, + -0.5000001, + 0, + 0, + 0.5000001, + 0.8660253, + 0, + 0, + 0.27093494, + 0.4692731, + 1 + ], + "objectMetadataList" : [ + + ] + }, + "D66134B1-3681-4A8E-AFE5-29F257229F3B" : { + "cameraTransform" : [ + 1, + 0, + -0, + 0, + -0, + 0.7071069, + -0.7071067, + 0, + 0, + 0.7071067, + 0.7071069, + 0, + 0, + 0.26894823, + 0.26934713, + 1 + ], + "objectMetadataList" : [ + [ + "D66134B1-3681-4A8E-AFE5-29F257229F3B", + "Root", + "GridMaterial", + "GridMaterial" + ], + { + "isExpanded" : true, + "isLocked" : false + }, + [ + "D66134B1-3681-4A8E-AFE5-29F257229F3B", + "Root" + ], + { + "isExpanded" : true, + "isLocked" : false + } + ] + } +} \ No newline at end of file diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata new file mode 100644 index 0000000..6dea95c --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata @@ -0,0 +1,17 @@ +{ + "cameraPresets" : { + + }, + "secondaryToolbarData" : { + "isGridVisible" : true, + "sceneReverbPreset" : -1 + }, + "unitDefaults" : { + "°" : "°", + "kg" : "g", + "m" : "cm", + "m\/s" : "m\/s", + "m\/s²" : "m\/s²", + "s" : "s" + } +} \ No newline at end of file diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.swift b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.swift new file mode 100644 index 0000000..d043ae1 --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version:5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "RealityKitContent", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "RealityKitContent", + targets: ["RealityKitContent"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "RealityKitContent", + dependencies: []), + ] +) \ No newline at end of file diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/README.md b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/README.md new file mode 100644 index 0000000..486b575 --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/README.md @@ -0,0 +1,3 @@ +# RealityKitContent + +A description of this package. \ No newline at end of file diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda new file mode 100644 index 0000000..37232ff --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda @@ -0,0 +1,50 @@ +#usda 1.0 +( + defaultPrim = "Root" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "Root" +{ + reorder nameChildren = ["Sphere_Left", "Sphere_Right", "_GridMaterial"] + def Sphere "Sphere_Right" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + rel material:binding = ( + bindMaterialAs = "weakerThanDescendants" + ) + double radius = 0.1 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + float3 xformOp:translate = (0.5, 1.5, -1.5) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "Sphere_Left" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + rel material:binding = ( + bindMaterialAs = "weakerThanDescendants" + ) + double radius = 0.1 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + float3 xformOp:translate = (-0.5, 1.5, -1.5) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def "_GridMaterial" ( + active = true + prepend references = @_GridMaterial.usda@ + ) + { + float3 xformOp:scale = (1, 1, 1) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } +} + diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda new file mode 100644 index 0000000..0600de4 --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda @@ -0,0 +1,59 @@ +#usda 1.0 +( + defaultPrim = "Root" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "Root" +{ + reorder nameChildren = ["GridMaterial", "Sphere"] + rel material:binding = None ( + bindMaterialAs = "weakerThanDescendants" + ) + + def Sphere "Sphere" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + rel material:binding = ( + bindMaterialAs = "weakerThanDescendants" + ) + double radius = 0.05 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + float3 xformOp:translate = (0, 0, 0.0004) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def RealityKitComponent "Collider" + { + uint group = 1 + uniform token info:id = "RealityKit.Collider" + uint mask = 4294967295 + token type = "Default" + + def RealityKitStruct "Shape" + { + float3 extent = (0.2, 0.2, 0.2) + float radius = 0.05 + token shapeType = "Sphere" + } + } + + def RealityKitComponent "InputTarget" + { + uniform token info:id = "RealityKit.InputTarget" + } + } + + def "_GridMaterial" ( + active = true + prepend references = @_GridMaterial.usda@ + ) + { + float3 xformOp:scale = (1, 1, 1) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } +} + diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_GridMaterial.usda b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_GridMaterial.usda new file mode 100644 index 0000000..b7afd02 --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_GridMaterial.usda @@ -0,0 +1,216 @@ +#usda 1.0 +( + defaultPrim = "Root" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "Root" +{ + def Material "GridMaterial" + { + reorder nameChildren = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "DefaultSurfaceShader", "MaterialXPreviewSurface", "Texcoord", "Add", "Multiply", "Fractional", "LineCounts", "Multiply_1", "Separate2", "Separate2_1", "Ifgreater", "Ifgreater_1", "Max", "Background_Color"] + token outputs:mtlx:surface.connect = + token outputs:realitykit:vertex + token outputs:surface + float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (2222, 300.5) + float2 ui:nodegraph:realitykit:subgraphOutputs:size = (182, 89) + int ui:nodegraph:realitykit:subgraphOutputs:stackingOrder = 749 + + def Shader "DefaultSurfaceShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (1, 1, 1) + float inputs:roughness = 0.75 + token outputs:surface + } + + def Shader "MaterialXPreviewSurface" + { + uniform token info:id = "ND_UsdPreviewSurface_surfaceshader" + float inputs:clearcoat + float inputs:clearcoatRoughness + color3f inputs:diffuseColor.connect = + color3f inputs:emissiveColor + float inputs:ior + float inputs:metallic = 0.15 + float3 inputs:normal + float inputs:occlusion + float inputs:opacity + float inputs:opacityThreshold + float inputs:roughness = 0.5 + token outputs:out + float2 ui:nodegraph:node:pos = (1967, 300.5) + float2 ui:nodegraph:node:size = (208, 297) + int ui:nodegraph:node:stackingOrder = 870 + string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["Advanced"] + } + + def Shader "Texcoord" + { + uniform token info:id = "ND_texcoord_vector2" + float2 outputs:out + float2 ui:nodegraph:node:pos = (94.14453, 35.29297) + float2 ui:nodegraph:node:size = (182, 43) + int ui:nodegraph:node:stackingOrder = 1358 + } + + def Shader "Multiply" + { + uniform token info:id = "ND_multiply_vector2" + float2 inputs:in1.connect = + float2 inputs:in2 = (32, 15) + float2 inputs:in2.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (275.64453, 47.29297) + float2 ui:nodegraph:node:size = (61, 36) + int ui:nodegraph:node:stackingOrder = 1348 + string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:in2"] + } + + def Shader "Fractional" + { + uniform token info:id = "ND_realitykit_fractional_vector2" + float2 inputs:in.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (440.5, 49.5) + float2 ui:nodegraph:node:size = (155, 99) + int ui:nodegraph:node:stackingOrder = 1345 + } + + def Shader "BaseColor" + { + uniform token info:id = "ND_constant_color3" + color3f inputs:value = (0.89737034, 0.89737034, 0.89737034) ( + colorSpace = "Input - Texture - sRGB - sRGB" + ) + color3f inputs:value.connect = None + color3f outputs:out + float2 ui:nodegraph:node:pos = (1537.5977, 363.07812) + float2 ui:nodegraph:node:size = (150, 43) + int ui:nodegraph:node:stackingOrder = 1353 + } + + def Shader "LineColor" + { + uniform token info:id = "ND_constant_color3" + color3f inputs:value = (0.55945957, 0.55945957, 0.55945957) ( + colorSpace = "Input - Texture - sRGB - sRGB" + ) + color3f inputs:value.connect = None + color3f outputs:out + float2 ui:nodegraph:node:pos = (1536.9844, 287.86328) + float2 ui:nodegraph:node:size = (146, 43) + int ui:nodegraph:node:stackingOrder = 1355 + } + + def Shader "LineWidths" + { + uniform token info:id = "ND_combine2_vector2" + float inputs:in1 = 0.1 + float inputs:in2 = 0.1 + float2 outputs:out + float2 ui:nodegraph:node:pos = (443.64453, 233.79297) + float2 ui:nodegraph:node:size = (151, 43) + int ui:nodegraph:node:stackingOrder = 1361 + } + + def Shader "LineCounts" + { + uniform token info:id = "ND_combine2_vector2" + float inputs:in1 = 24 + float inputs:in2 = 12 + float2 outputs:out + float2 ui:nodegraph:node:pos = (94.14453, 138.29297) + float2 ui:nodegraph:node:size = (153, 43) + int ui:nodegraph:node:stackingOrder = 1359 + } + + def Shader "Remap" + { + uniform token info:id = "ND_remap_color3" + color3f inputs:in.connect = + color3f inputs:inhigh.connect = None + color3f inputs:inlow.connect = None + color3f inputs:outhigh.connect = + color3f inputs:outlow.connect = + color3f outputs:out + float2 ui:nodegraph:node:pos = (1755.5, 300.5) + float2 ui:nodegraph:node:size = (95, 171) + int ui:nodegraph:node:stackingOrder = 1282 + string[] ui:nodegraph:realitykit:node:attributesShowingChildren = ["inputs:outlow"] + } + + def Shader "Separate2" + { + uniform token info:id = "ND_separate2_vector2" + float2 inputs:in.connect = + float outputs:outx + float outputs:outy + float2 ui:nodegraph:node:pos = (1212.6445, 128.91797) + float2 ui:nodegraph:node:size = (116, 117) + int ui:nodegraph:node:stackingOrder = 1363 + } + + def Shader "Combine3" + { + uniform token info:id = "ND_combine3_color3" + float inputs:in1.connect = + float inputs:in2.connect = + float inputs:in3.connect = + color3f outputs:out + float2 ui:nodegraph:node:pos = (1578.1445, 128.91797) + float2 ui:nodegraph:node:size = (146, 54) + int ui:nodegraph:node:stackingOrder = 1348 + } + + def Shader "Range" + { + uniform token info:id = "ND_range_vector2" + bool inputs:doclamp = 1 + float2 inputs:gamma = (2, 2) + float2 inputs:in.connect = + float2 inputs:inhigh.connect = + float2 inputs:inlow = (0.02, 0.02) + float2 inputs:outhigh + float2 inputs:outlow + float2 outputs:out + float2 ui:nodegraph:node:pos = (990.64453, 128.91797) + float2 ui:nodegraph:node:size = (98, 207) + int ui:nodegraph:node:stackingOrder = 1364 + } + + def Shader "Subtract" + { + uniform token info:id = "ND_subtract_vector2" + float2 inputs:in1.connect = + float2 inputs:in2.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (612.64453, 87.04297) + float2 ui:nodegraph:node:size = (63, 36) + int ui:nodegraph:node:stackingOrder = 1348 + } + + def Shader "Absval" + { + uniform token info:id = "ND_absval_vector2" + float2 inputs:in.connect = + float2 outputs:out + float2 ui:nodegraph:node:pos = (765.64453, 87.04297) + float2 ui:nodegraph:node:size = (123, 43) + int ui:nodegraph:node:stackingOrder = 1348 + } + + def Shader "Min" + { + uniform token info:id = "ND_min_float" + float inputs:in1.connect = + float inputs:in2.connect = + float outputs:out + float2 ui:nodegraph:node:pos = (1388.1445, 128.91797) + float2 ui:nodegraph:node:size = (114, 36) + int ui:nodegraph:node:stackingOrder = 1363 + } + } +} + diff --git a/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift new file mode 100644 index 0000000..5caba4e --- /dev/null +++ b/Examples/visionOS/VisionConnect/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift @@ -0,0 +1,4 @@ +import Foundation + +/// Bundle for the RealityKitContent project +public let realityKitContentBundle = Bundle.module diff --git a/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.pbxproj b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.pbxproj new file mode 100644 index 0000000..bdec36b --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.pbxproj @@ -0,0 +1,386 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 40B70D892AA3C4AE00785A9C /* RealityKitContent in Frameworks */ = {isa = PBXBuildFile; productRef = 40B70D882AA3C4AE00785A9C /* RealityKitContent */; }; + 40B70D8B2AA3C4AE00785A9C /* VisionConnectApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B70D8A2AA3C4AE00785A9C /* VisionConnectApp.swift */; }; + 40B70D8D2AA3C4AE00785A9C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B70D8C2AA3C4AE00785A9C /* ContentView.swift */; }; + 40B70D8F2AA3C4AE00785A9C /* ImmersiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B70D8E2AA3C4AE00785A9C /* ImmersiveView.swift */; }; + 40B70D912AA3C4AF00785A9C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40B70D902AA3C4AF00785A9C /* Assets.xcassets */; }; + 40B70D942AA3C4AF00785A9C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40B70D932AA3C4AF00785A9C /* Preview Assets.xcassets */; }; + 40B70D9D2AA3C4DC00785A9C /* RealityCheckConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 40B70D9C2AA3C4DC00785A9C /* RealityCheckConnect */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 40B70D832AA3C4AE00785A9C /* VisionConnect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisionConnect.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 40B70D872AA3C4AE00785A9C /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = ""; }; + 40B70D8A2AA3C4AE00785A9C /* VisionConnectApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionConnectApp.swift; sourceTree = ""; }; + 40B70D8C2AA3C4AE00785A9C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 40B70D8E2AA3C4AE00785A9C /* ImmersiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmersiveView.swift; sourceTree = ""; }; + 40B70D902AA3C4AF00785A9C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 40B70D932AA3C4AF00785A9C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 40B70D952AA3C4AF00785A9C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 40B70D802AA3C4AE00785A9C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40B70D892AA3C4AE00785A9C /* RealityKitContent in Frameworks */, + 40B70D9D2AA3C4DC00785A9C /* RealityCheckConnect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 40B70D7A2AA3C4AE00785A9C = { + isa = PBXGroup; + children = ( + 40B70D852AA3C4AE00785A9C /* VisionConnect */, + 40B70D862AA3C4AE00785A9C /* Packages */, + 40B70D842AA3C4AE00785A9C /* Products */, + 40B70D9B2AA3C4DC00785A9C /* Frameworks */, + ); + sourceTree = ""; + }; + 40B70D842AA3C4AE00785A9C /* Products */ = { + isa = PBXGroup; + children = ( + 40B70D832AA3C4AE00785A9C /* VisionConnect.app */, + ); + name = Products; + sourceTree = ""; + }; + 40B70D852AA3C4AE00785A9C /* VisionConnect */ = { + isa = PBXGroup; + children = ( + 40B70D8A2AA3C4AE00785A9C /* VisionConnectApp.swift */, + 40B70D8C2AA3C4AE00785A9C /* ContentView.swift */, + 40B70D8E2AA3C4AE00785A9C /* ImmersiveView.swift */, + 40B70D902AA3C4AF00785A9C /* Assets.xcassets */, + 40B70D952AA3C4AF00785A9C /* Info.plist */, + 40B70D922AA3C4AF00785A9C /* Preview Content */, + ); + path = VisionConnect; + sourceTree = ""; + }; + 40B70D862AA3C4AE00785A9C /* Packages */ = { + isa = PBXGroup; + children = ( + 40B70D872AA3C4AE00785A9C /* RealityKitContent */, + ); + path = Packages; + sourceTree = ""; + }; + 40B70D922AA3C4AF00785A9C /* Preview Content */ = { + isa = PBXGroup; + children = ( + 40B70D932AA3C4AF00785A9C /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 40B70D9B2AA3C4DC00785A9C /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 40B70D822AA3C4AE00785A9C /* VisionConnect */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40B70D982AA3C4AF00785A9C /* Build configuration list for PBXNativeTarget "VisionConnect" */; + buildPhases = ( + 40B70D7F2AA3C4AE00785A9C /* Sources */, + 40B70D802AA3C4AE00785A9C /* Frameworks */, + 40B70D812AA3C4AE00785A9C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VisionConnect; + packageProductDependencies = ( + 40B70D882AA3C4AE00785A9C /* RealityKitContent */, + 40B70D9C2AA3C4DC00785A9C /* RealityCheckConnect */, + ); + productName = VisionConnect; + productReference = 40B70D832AA3C4AE00785A9C /* VisionConnect.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 40B70D7B2AA3C4AE00785A9C /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + 40B70D822AA3C4AE00785A9C = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = 40B70D7E2AA3C4AE00785A9C /* Build configuration list for PBXProject "VisionConnect" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 40B70D7A2AA3C4AE00785A9C; + productRefGroup = 40B70D842AA3C4AE00785A9C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 40B70D822AA3C4AE00785A9C /* VisionConnect */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 40B70D812AA3C4AE00785A9C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40B70D942AA3C4AF00785A9C /* Preview Assets.xcassets in Resources */, + 40B70D912AA3C4AF00785A9C /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 40B70D7F2AA3C4AE00785A9C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40B70D8D2AA3C4AE00785A9C /* ContentView.swift in Sources */, + 40B70D8B2AA3C4AE00785A9C /* VisionConnectApp.swift in Sources */, + 40B70D8F2AA3C4AE00785A9C /* ImmersiveView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 40B70D962AA3C4AF00785A9C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = xros; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Debug; + }; + 40B70D972AA3C4AF00785A9C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = xros; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Release; + }; + 40B70D992AA3C4AF00785A9C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"VisionConnect/Preview Content\""; + DEVELOPMENT_TEAM = M92A6H7EPZ; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.VisionConnect; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Debug; + }; + 40B70D9A2AA3C4AF00785A9C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"VisionConnect/Preview Content\""; + DEVELOPMENT_TEAM = M92A6H7EPZ; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.monstarlab.VisionConnect; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 40B70D7E2AA3C4AE00785A9C /* Build configuration list for PBXProject "VisionConnect" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40B70D962AA3C4AF00785A9C /* Debug */, + 40B70D972AA3C4AF00785A9C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40B70D982AA3C4AF00785A9C /* Build configuration list for PBXNativeTarget "VisionConnect" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40B70D992AA3C4AF00785A9C /* Debug */, + 40B70D9A2AA3C4AF00785A9C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 40B70D882AA3C4AE00785A9C /* RealityKitContent */ = { + isa = XCSwiftPackageProductDependency; + productName = RealityKitContent; + }; + 40B70D9C2AA3C4DC00785A9C /* RealityCheckConnect */ = { + isa = XCSwiftPackageProductDependency; + productName = RealityCheckConnect; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 40B70D7B2AA3C4AE00785A9C /* Project object */; +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/App/AppPackage/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/xcshareddata/xcschemes/VisionConnect.xcscheme similarity index 64% rename from App/AppPackage/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme rename to Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/xcshareddata/xcschemes/VisionConnect.xcscheme index a9f3fc6..de0a785 100644 --- a/App/AppPackage/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme +++ b/Examples/visionOS/VisionConnect/VisionConnect.xcodeproj/xcshareddata/xcschemes/VisionConnect.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "40B70D822AA3C4AE00785A9C" + BuildableName = "VisionConnect.app" + BlueprintName = "VisionConnect" + ReferencedContainer = "container:VisionConnect.xcodeproj"> @@ -28,18 +28,6 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" shouldAutocreateTestPlan = "YES"> - - - - - - + + + + - + + BlueprintIdentifier = "40B70D822AA3C4AE00785A9C" + BuildableName = "VisionConnect.app" + BlueprintName = "VisionConnect" + ReferencedContainer = "container:VisionConnect.xcodeproj"> - + diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0c7eecb --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 0000000..950af4d --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + }, + { + "filename" : "Back.solidimagestacklayer" + } + ] +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0c7eecb --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0c7eecb --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/visionOS/VisionConnect/VisionConnect/ContentView.swift b/Examples/visionOS/VisionConnect/VisionConnect/ContentView.swift new file mode 100644 index 0000000..af2951d --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/ContentView.swift @@ -0,0 +1,64 @@ +// +// ContentView.swift +// VisionConnect +// +// Created by Cristian Díaz on 02.09.23. +// + +import SwiftUI +import RealityKit +import RealityKitContent +import RealityCheckConnect + +struct ContentView: View { + @State + private var showImmersiveSpace = false + + @State + private var immersiveSpaceIsShown = false + + @Environment(\.openImmersiveSpace) + var openImmersiveSpace + + @Environment(\.dismissImmersiveSpace) + var dismissImmersiveSpace + + var body: some View { + VStack { + Model3D(named: "Scene", bundle: realityKitContentBundle) + .padding(.bottom, 50) + .realityCheck() + .accessibilityElement() + .accessibilityValue("Accessible Sphere") + + Text("Hello, world!") + + Toggle("Show Immersive Space", isOn: $showImmersiveSpace) + .toggleStyle(.button) + .padding(.top, 50) + } + .padding() + .onChange(of: showImmersiveSpace) { _, newValue in + Task { + if newValue { + switch await openImmersiveSpace(id: "ImmersiveSpace") { + case .opened: + immersiveSpaceIsShown = true + case .error, .userCancelled: + fallthrough + @unknown default: + immersiveSpaceIsShown = false + showImmersiveSpace = false + } + } else if immersiveSpaceIsShown { + await dismissImmersiveSpace() + immersiveSpaceIsShown = false + } + } + } + } +} + +#Preview(windowStyle: .automatic) { + ContentView() +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/ImmersiveView.swift b/Examples/visionOS/VisionConnect/VisionConnect/ImmersiveView.swift new file mode 100644 index 0000000..5a4c9d8 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/ImmersiveView.swift @@ -0,0 +1,26 @@ +// +// ImmersiveView.swift +// VisionConnect +// +// Created by Cristian Díaz on 02.09.23. +// + +import SwiftUI +import RealityKit +import RealityKitContent + +struct ImmersiveView: View { + var body: some View { + RealityView { content in + // Add the initial RealityKit content + if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) { + content.add(scene) + } + } + } +} + +#Preview { + ImmersiveView() + .previewLayout(.sizeThatFits) +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Info.plist b/Examples/visionOS/VisionConnect/VisionConnect/Info.plist new file mode 100644 index 0000000..6cb9bc1 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Info.plist @@ -0,0 +1,20 @@ + + + + + NSBonjourServices + + _reality-check._tcp + _reality-check._udp + + UIApplicationSceneManifest + + UIApplicationPreferredDefaultSceneSessionRole + UIWindowSceneSessionRoleApplication + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + + diff --git a/Examples/visionOS/VisionConnect/VisionConnect/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/visionOS/VisionConnect/VisionConnect/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/visionOS/VisionConnect/VisionConnect/VisionConnectApp.swift b/Examples/visionOS/VisionConnect/VisionConnect/VisionConnectApp.swift new file mode 100644 index 0000000..3250dbc --- /dev/null +++ b/Examples/visionOS/VisionConnect/VisionConnect/VisionConnectApp.swift @@ -0,0 +1,25 @@ +// +// VisionConnectApp.swift +// VisionConnect +// +// Created by Cristian Díaz on 02.09.23. +// + +import SwiftUI +import RealityCheckConnect + +@main +struct VisionConnectApp: App { + @State var realityCheckConnectModel = RealityCheckConnectViewModel() + + var body: some Scene { + WindowGroup { + ContentView() + .environment(realityCheckConnectModel) + } + + ImmersiveSpace(id: "ImmersiveSpace") { + ImmersiveView() + } + } +} diff --git a/Package.swift b/Package.swift index 85dffa9..06a0fa8 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,42 +6,24 @@ import PackageDescription let package = Package( name: "reality-check", platforms: [ - .macOS(.v12), .iOS(.v15), + .macOS(.v12), + .visionOS(.v1), ], products: [ .library( name: "RealityCheckConnect", - targets: [ - "RealityCheckConnect", - "Models", - "MultipeerClient", - "RealityDumpClient", - "StreamingClient", - ] + targets: ["RealityCheckConnect"] ), ], dependencies: [ - .package( - url: "https://github.com/pointfreeco/swift-custom-dump", - from: "0.10.3" - ), - .package( - url: "https://github.com/pointfreeco/swift-dependencies", - from: "0.5.0" - ), - .package( - url: "https://github.com/devicekit/DeviceKit.git", - from: "5.0.0" - ), + .package(url: "https://github.com/devicekit/DeviceKit.git", from: "5.0.0"), + .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"), + .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.0.0"), + .package(url: "https://github.com/elkraneo/reality-dump", branch: "main"), ], targets: [ - .target( - name: "Models", - dependencies: [ - .product(name: "CustomDump", package: "swift-custom-dump") - ] - ), + .target(name: "Models"), .target( name: "MultipeerClient", dependencies: [ @@ -56,20 +38,36 @@ let package = Package( name: "RealityCheckConnect", dependencies: [ .product(name: "Dependencies", package: "swift-dependencies"), - .product( - name: "DeviceKit", - package: "DeviceKit", - condition: .when(platforms: [.iOS]) - ), + .target(name: "RealityCheckConnect_visionOS", condition: .when(platforms: [.visionOS])), + .target(name: "RealityCheckConnect_iOS", condition: .when(platforms: [.iOS])), //FIXME: still compiled for `visionOS` + ] + ), + .target( + name: "RealityCheckConnect_iOS", + dependencies: [ + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DeviceKit", package: "DeviceKit", condition: .when(platforms: [.iOS])), "Models", "MultipeerClient", "RealityDumpClient", "StreamingClient", ] ), + .target( + name: "RealityCheckConnect_visionOS", + dependencies: [ + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "RealityDump", package: "reality-dump"), + "Models", + "MultipeerClient", + "RealityDumpClient", //TODO: rename and export giving only Codable responsabilities + "StreamingClient", + ] + ), .target( name: "RealityDumpClient", dependencies: [ + .product(name: "CustomDump", package: "swift-custom-dump"), .product(name: "Dependencies", package: "swift-dependencies"), "Models", ] diff --git a/RealityCheck.xcworkspace/contents.xcworkspacedata b/RealityCheck.xcworkspace/contents.xcworkspacedata index d29b272..a94c39a 100644 --- a/RealityCheck.xcworkspace/contents.xcworkspacedata +++ b/RealityCheck.xcworkspace/contents.xcworkspacedata @@ -10,17 +10,31 @@ - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/RealityCheck.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RealityCheck.xcworkspace/xcshareddata/swiftpm/Package.resolved index 49fe812..8d992c6 100644 --- a/RealityCheck.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RealityCheck.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,104 +1,124 @@ { - "pins" : [ - { - "identity" : "combine-schedulers", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/combine-schedulers", - "state" : { - "revision" : "0625932976b3ae23949f6b816d13bd97f3b40b7c", - "version" : "0.10.0" + "object": { + "pins": [ + { + "package": "combine-schedulers", + "repositoryURL": "https://github.com/pointfreeco/combine-schedulers", + "state": { + "branch": null, + "revision": "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", + "version": "1.0.0" + } + }, + { + "package": "DeviceKit", + "repositoryURL": "https://github.com/devicekit/DeviceKit.git", + "state": { + "branch": null, + "revision": "691fe8112cca20ebf0020a1709d4e0205400311c", + "version": "5.0.0" + } + }, + { + "package": "reality-dump", + "repositoryURL": "https://github.com/elkraneo/reality-dump", + "state": { + "branch": "main", + "revision": "a0fea328f3618e701bfd5f4600d16cd187c5e333", + "version": null + } + }, + { + "package": "swift-case-paths", + "repositoryURL": "https://github.com/pointfreeco/swift-case-paths", + "state": { + "branch": null, + "revision": "5da6989aae464f324eef5c5b52bdb7974725ab81", + "version": "1.0.0" + } + }, + { + "package": "swift-clocks", + "repositoryURL": "https://github.com/pointfreeco/swift-clocks", + "state": { + "branch": null, + "revision": "d1fd837326aa719bee979bdde1f53cd5797443eb", + "version": "1.0.0" + } + }, + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections", + "state": { + "branch": null, + "revision": "937e904258d22af6e447a0b72c0bc67583ef64a2", + "version": "1.0.4" + } + }, + { + "package": "swift-composable-architecture", + "repositoryURL": "https://github.com/pointfreeco/swift-composable-architecture", + "state": { + "branch": null, + "revision": "a7c1f799b55ecb418f85094b142565834f7ee7c7", + "version": "1.2.0" + } + }, + { + "package": "swift-concurrency-extras", + "repositoryURL": "https://github.com/pointfreeco/swift-concurrency-extras", + "state": { + "branch": null, + "revision": "ea631ce892687f5432a833312292b80db238186a", + "version": "1.0.0" + } + }, + { + "package": "swift-custom-dump", + "repositoryURL": "https://github.com/pointfreeco/swift-custom-dump", + "state": { + "branch": null, + "revision": "edd66cace818e1b1c6f1b3349bb1d8e00d6f8b01", + "version": "1.0.0" + } + }, + { + "package": "swift-dependencies", + "repositoryURL": "https://github.com/pointfreeco/swift-dependencies", + "state": { + "branch": null, + "revision": "4e1eb6e28afe723286d8cc60611237ffbddba7c5", + "version": "1.0.0" + } + }, + { + "package": "swift-identified-collections", + "repositoryURL": "https://github.com/pointfreeco/swift-identified-collections", + "state": { + "branch": null, + "revision": "d1e45f3e1eee2c9193f5369fa9d70a6ddad635e8", + "version": "1.0.0" + } + }, + { + "package": "swiftui-navigation", + "repositoryURL": "https://github.com/pointfreeco/swiftui-navigation", + "state": { + "branch": null, + "revision": "6eb293c49505d86e9e24232cb6af6be7fff93bd5", + "version": "1.0.2" + } + }, + { + "package": "xctest-dynamic-overlay", + "repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state": { + "branch": null, + "revision": "302891700c7fa3b92ebde9fe7b42933f8349f3c7", + "version": "1.0.0" + } } - }, - { - "identity" : "devicekit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/devicekit/DeviceKit.git", - "state" : { - "revision" : "691fe8112cca20ebf0020a1709d4e0205400311c", - "version" : "5.0.0" - } - }, - { - "identity" : "swift-case-paths", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-case-paths", - "state" : { - "revision" : "fc45e7b2cfece9dd80b5a45e6469ffe67fe67984", - "version" : "0.14.1" - } - }, - { - "identity" : "swift-clocks", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-clocks", - "state" : { - "revision" : "f9acfa1a45f4483fe0f2c434a74e6f68f865d12d", - "version" : "0.3.0" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections", - "state" : { - "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", - "version" : "1.0.4" - } - }, - { - "identity" : "swift-composable-architecture", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-composable-architecture", - "state" : { - "branch" : "prerelease/1.0", - "revision" : "d9ea2dc5b1d7653c5018304906b4e62694c47dfc" - } - }, - { - "identity" : "swift-custom-dump", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-custom-dump", - "state" : { - "revision" : "505aa98716275fbd045d8f934fee3337c82ffbd3", - "version" : "0.10.3" - } - }, - { - "identity" : "swift-dependencies", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-dependencies", - "state" : { - "revision" : "de1a984a71e51f6e488e98ce3652035563eb8acb", - "version" : "0.5.1" - } - }, - { - "identity" : "swift-identified-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-identified-collections", - "state" : { - "revision" : "f52eee28bdc6065aa2f8424067e6f04c74bda6e6", - "version" : "0.7.1" - } - }, - { - "identity" : "swiftui-navigation", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swiftui-navigation", - "state" : { - "revision" : "db81007362f998654239021ca9308a264e59d3e2", - "version" : "0.7.2" - } - }, - { - "identity" : "xctest-dynamic-overlay", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", - "state" : { - "revision" : "4af50b38daf0037cfbab15514a241224c3f62f98", - "version" : "0.8.5" - } - } - ], - "version" : 2 + ] + }, + "version": 1 } diff --git a/Sources/Models/Codable Extras/CodableFloat4x4.swift b/Sources/Models/Codable Extras/CodableFloat4x4.swift index 8291607..f1c2072 100644 --- a/Sources/Models/Codable Extras/CodableFloat4x4.swift +++ b/Sources/Models/Codable Extras/CodableFloat4x4.swift @@ -1,4 +1,3 @@ -import CustomDump import simd public struct CodableFloat4x4: Codable { @@ -43,8 +42,9 @@ extension CodableFloat4x4: CustomDebugStringConvertible { } } -extension CodableFloat4x4: CustomDumpStringConvertible { - public var customDumpDescription: String { - "\(self.float4x4)" - } -} +//FIXME: +//extension CodableFloat4x4: CustomDumpStringConvertible { +// public var customDumpDescription: String { +// "\(self.float4x4)" +// } +//} diff --git a/Sources/Models/Codable Extras/CodableQuaternion.swift b/Sources/Models/Codable Extras/CodableQuaternion.swift index 1409eef..4810dbf 100644 --- a/Sources/Models/Codable Extras/CodableQuaternion.swift +++ b/Sources/Models/Codable Extras/CodableQuaternion.swift @@ -1,4 +1,3 @@ -import CustomDump import simd // Wrapper struct for simd_quatf to make it Codable @@ -41,8 +40,9 @@ extension CodableQuaternion: CustomDebugStringConvertible { } } -extension CodableQuaternion: CustomDumpStringConvertible { - public var customDumpDescription: String { - "\(self.vector)" - } -} +//FIXME: +//extension CodableQuaternion: CustomDumpStringConvertible { +// public var customDumpDescription: String { +// "\(self.vector)" +// } +//} diff --git a/Sources/Models/CodableARView.swift b/Sources/Models/CodableARView.swift index c04eb8c..afacd62 100644 --- a/Sources/Models/CodableARView.swift +++ b/Sources/Models/CodableARView.swift @@ -4,47 +4,41 @@ import RealityKit public struct CodableScene: Codable, Equatable { public let anchors: [IdentifiableEntity] - init( + public init( anchors: [IdentifiableEntity] ) { self.anchors = anchors } - - //TODO: allow anchors identification at this level - // init( - // _ scene: RealityKit.Scene - // ) { - // self.anchors = ??? - // } } -extension RealityKit.ARView.DebugOptions: Codable {} +#if !os(visionOS) + extension RealityKit.ARView.DebugOptions: Codable {} -public struct CodableARView: Codable, Equatable { - //MARK: Working with the Scene - public let scene: CodableScene + public struct CodableARView: Codable, Equatable { + //MARK: Working with the Scene + public let scene: CodableScene - //MARK: Debugging the Session - ///The current debugging options. - public let debugOptionsRawValue: RealityKit.ARView.DebugOptions.RawValue + //MARK: Debugging the Session + ///The current debugging options. + public let debugOptionsRawValue: RealityKit.ARView.DebugOptions.RawValue - /// #Managing the View + /// #Managing the View - // The scale factor of the content in the view. - public let contentScaleFactor: CGFloat + // The scale factor of the content in the view. + public let contentScaleFactor: CGFloat - public init( - _ arView: RealityKit.ARView, - anchors: [IdentifiableEntity], - contentScaleFactor: CGFloat - ) { - self.scene = CodableScene(anchors: anchors) - self.debugOptionsRawValue = arView.debugOptions.rawValue - self.contentScaleFactor = contentScaleFactor + public init( + _ arView: RealityKit.ARView, + anchors: [IdentifiableEntity], + contentScaleFactor: CGFloat + ) { + self.scene = CodableScene(anchors: anchors) + self.debugOptionsRawValue = arView.debugOptions.rawValue + self.contentScaleFactor = contentScaleFactor + } } -} -/* + /* /// #Working with the Scene var scene: Scene // The scene that the view renders and simulates. @@ -88,3 +82,31 @@ public struct CodableARView: Codable, Equatable { var debugOptions: ARView.DebugOptions The current debugging options. */ + + extension ARView { + public func findEntityIdentified(targetID: UInt64) -> Entity? { + for anchor in self.scene.anchors { + if let entity = findIdentifiableEntity(root: anchor, targetID: targetID) { + return entity + } + } + + return nil + } + } + + public func findIdentifiableEntity(root: RealityKit.Entity, targetID: UInt64) -> Entity? { + if root.id == targetID { + return root + } + + for child in root.children { + if let foundNode = findIdentifiableEntity(root: child, targetID: targetID) { + return foundNode + } + } + + return nil + } + +#endif diff --git a/Sources/Models/CodableScene.swift b/Sources/Models/CodableScene.swift new file mode 100644 index 0000000..ce10d5f --- /dev/null +++ b/Sources/Models/CodableScene.swift @@ -0,0 +1,5 @@ +import RealityKit + +//public struct CodableScene: Codable, Equatable { +// +//} diff --git a/Sources/Models/Component/ComponentSet+Equatable.swift b/Sources/Models/Component/ComponentSet+Equatable.swift deleted file mode 100644 index ad8d373..0000000 --- a/Sources/Models/Component/ComponentSet+Equatable.swift +++ /dev/null @@ -1,9 +0,0 @@ -import RealityKit - -extension Entity.ComponentSet: Equatable { - //FIXME: Find a better way to use equatable or use another type instear `Entity.ComponentSet` - - public static func == (lhs: Entity.ComponentSet, rhs: Entity.ComponentSet) -> Bool { - lhs.count == rhs.count - } -} diff --git a/Sources/Models/Component/IdentifiableComponent+Type.swift b/Sources/Models/Component/IdentifiableComponent+Type.swift index 2f54e91..bdcc42b 100644 --- a/Sources/Models/Component/IdentifiableComponent+Type.swift +++ b/Sources/Models/Component/IdentifiableComponent+Type.swift @@ -1,191 +1,218 @@ import RealityKit extension IdentifiableComponent.ComponentType: RawRepresentable { - public var rawValue: Component.Type { - switch self { - case .anchoring: - return AnchoringComponent.self - - case .characterController: - return CharacterControllerComponent.self - - case .characterControllerState: - return CharacterControllerStateComponent.self - - case .collision: - return CollisionComponent.self - - case .directionalLight: - return DirectionalLightComponent.self - - case .directionalLightShadow: - return DirectionalLightComponent.Shadow.self - - case .model: - return ModelComponent.self - - case .modelDebugOptions: - return ModelDebugOptionsComponent.self - - case .perspectiveCamera: - return PerspectiveCameraComponent.self - - case .physicsBody: - return PhysicsBodyComponent.self - - case .physicsMotion: - return PhysicsMotionComponent.self - - case .pointLight: - return PointLightComponent.self - - case .spotLight: - return SpotLightComponent.self - - case .spotLightShadow: - return SpotLightComponent.Shadow.self - - case .synchronization: - return SynchronizationComponent.self - - case .transform: - return Transform.self - } + public var rawValue: Component.Type { + switch self { + case .anchoring: + return AnchoringComponent.self + + case .characterController: + return CharacterControllerComponent.self + + case .characterControllerState: + return CharacterControllerStateComponent.self + + case .collision: + return CollisionComponent.self + + case .directionalLight: + #if !os(xrOS) + return DirectionalLightComponent.self + #else + //FIXME: xrOS compatibility + return AnchoringComponent.self + #endif + + case .directionalLightShadow: + #if !os(xrOS) + return DirectionalLightComponent.Shadow.self + #else + //FIXME: xrOS compatibility + return AnchoringComponent.self + #endif + + case .model: + return ModelComponent.self + + case .modelDebugOptions: + return ModelDebugOptionsComponent.self + + case .perspectiveCamera: + return PerspectiveCameraComponent.self + + case .physicsBody: + return PhysicsBodyComponent.self + + case .physicsMotion: + return PhysicsMotionComponent.self + + case .pointLight: + #if !os(xrOS) + return PointLightComponent.self + #else + //FIXME: xrOS compatibility + return AnchoringComponent.self + #endif + + case .spotLight: + #if !os(xrOS) + return SpotLightComponent.self + #else + //FIXME: xrOS compatibility + return AnchoringComponent.self + #endif + + case .spotLightShadow: + #if !os(xrOS) + return SpotLightComponent.Shadow.self + #else + //FIXME: xrOS compatibility + return AnchoringComponent.self + #endif + + case .synchronization: + return SynchronizationComponent.self + + case .transform: + return Transform.self } - - public init?(rawValue: Component.Type) { - for componentType in Self.allCases { - if componentType.rawValue == rawValue { - self = componentType - return - } - } - //TODO: handle unknown components - fatalError("Unknown Component.Type") + } + + public init?( + rawValue: Component.Type + ) { + for componentType in Self.allCases { + if componentType.rawValue == rawValue { + self = componentType + return + } } + //TODO: handle unknown components + fatalError("Unknown Component.Type") + } } extension IdentifiableComponent.ComponentType: CustomStringConvertible { - public var description: String { - switch self { - case .anchoring: - return "AnchoringComponent" + public var description: String { + switch self { + case .anchoring: + return "AnchoringComponent" - case .characterController: - return "CharacterControllerComponent" + case .characterController: + return "CharacterControllerComponent" - case .characterControllerState: - return "CharacterControllerStateComponent" + case .characterControllerState: + return "CharacterControllerStateComponent" - case .collision: - return "CollisionComponent" + case .collision: + return "CollisionComponent" - case .directionalLight: - return "DirectionalLightComponent" + case .directionalLight: + return "DirectionalLightComponent" - case .directionalLightShadow: - return "DirectionalLightComponent.Shadow" + case .directionalLightShadow: + return "DirectionalLightComponent.Shadow" - case .model: - return "ModelComponent" + case .model: + return "ModelComponent" - case .modelDebugOptions: - return "ModelDebugOptionsComponent" + case .modelDebugOptions: + return "ModelDebugOptionsComponent" - case .perspectiveCamera: - return "PerspectiveCameraComponent" + case .perspectiveCamera: + return "PerspectiveCameraComponent" - case .physicsBody: - return "PhysicsBodyComponent" + case .physicsBody: + return "PhysicsBodyComponent" - case .physicsMotion: - return "PhysicsMotionComponent" + case .physicsMotion: + return "PhysicsMotionComponent" - case .pointLight: - return "PointLightComponent" + case .pointLight: + return "PointLightComponent" - case .spotLight: - return "SpotLightComponent" + case .spotLight: + return "SpotLightComponent" - case .spotLightShadow: - return "SpotLightComponent.Shadow" + case .spotLightShadow: + return "SpotLightComponent.Shadow" - case .synchronization: - return "SynchronizationComponent" + case .synchronization: + return "SynchronizationComponent" - case .transform: - return "Transform" - } + case .transform: + return "Transform" } + } } extension IdentifiableComponent.ComponentType { - public var help: String { - switch self { - case .anchoring: - return """ - A description of how virtual content can be anchored to the real world. - """ - case .characterController: - return """ - A component that manages character movement. - """ - case .characterControllerState: - return """ - An object that maintains state for a character controller. - """ - case .collision: - return """ - A component that gives an entity the ability to collide with other entities that also have collision components. - """ - case .directionalLight: - return """ - A component that defines a directional light source. - """ - case .directionalLightShadow: - return """ - Defines shadow characteristics for a directional light. - """ - case .model: - return """ - A collection of resources that create the visual appearance of an entity. - """ - case .modelDebugOptions: - return """ - A component that changes how RealityKit renders its entity to help with debugging. - """ - case .perspectiveCamera: - return """ - In AR applications, the camera is automatically provided by the system. In non-AR scenarios, the camera needs to be set by the app. (If no camera is provided by the app, the system will use default camera.) - """ - case .physicsBody: - return """ - A component that defines an entity’s behavior in physics body simulations. - """ - case .physicsMotion: - return """ - A component that controls the motion of the body in physics simulations. - """ - case .pointLight: - return """ - A component that defines a point light source. - """ - case .spotLight: - return """ - A component that defines a spotlight source. - """ - case .spotLightShadow: - return """ - Characteristics of a shadow for the spotlight. - """ - case .synchronization: - return """ - A component that synchronizes an entity between processes and networked applications. - """ - case .transform: - return """ - A component that defines the scale, rotation, and translation of an entity. - """ - } + public var help: String { + switch self { + case .anchoring: + return """ + A description of how virtual content can be anchored to the real world. + """ + case .characterController: + return """ + A component that manages character movement. + """ + case .characterControllerState: + return """ + An object that maintains state for a character controller. + """ + case .collision: + return """ + A component that gives an entity the ability to collide with other entities that also have collision components. + """ + case .directionalLight: + return """ + A component that defines a directional light source. + """ + case .directionalLightShadow: + return """ + Defines shadow characteristics for a directional light. + """ + case .model: + return """ + A collection of resources that create the visual appearance of an entity. + """ + case .modelDebugOptions: + return """ + A component that changes how RealityKit renders its entity to help with debugging. + """ + case .perspectiveCamera: + return """ + In AR applications, the camera is automatically provided by the system. In non-AR scenarios, the camera needs to be set by the app. (If no camera is provided by the app, the system will use default camera.) + """ + case .physicsBody: + return """ + A component that defines an entity’s behavior in physics body simulations. + """ + case .physicsMotion: + return """ + A component that controls the motion of the body in physics simulations. + """ + case .pointLight: + return """ + A component that defines a point light source. + """ + case .spotLight: + return """ + A component that defines a spotlight source. + """ + case .spotLightShadow: + return """ + Characteristics of a shadow for the spotlight. + """ + case .synchronization: + return """ + A component that synchronizes an entity between processes and networked applications. + """ + case .transform: + return """ + A component that defines the scale, rotation, and translation of an entity. + """ } + } } diff --git a/Sources/Models/Component/IdentifiableComponent.swift b/Sources/Models/Component/IdentifiableComponent.swift index a68c5d9..23b07ac 100644 --- a/Sources/Models/Component/IdentifiableComponent.swift +++ b/Sources/Models/Component/IdentifiableComponent.swift @@ -1,4 +1,3 @@ -import CustomDump import RealityKit public struct IdentifiableComponent: Codable { @@ -76,22 +75,32 @@ public struct IdentifiableComponent: Codable { ) case .directionalLight: - let component = component as! DirectionalLightComponent - self.properties = .directionalLight( - DirectionalLightComponentProperties( - intensity: component.intensity, - isRealWorldProxy: component.isRealWorldProxy + #if os(iOS) && !os(xrOS) + let component = component as! DirectionalLightComponent + self.properties = .directionalLight( + DirectionalLightComponentProperties( + intensity: component.intensity, + isRealWorldProxy: component.isRealWorldProxy + ) ) - ) + #else + //FIXME: xrOS compatibility + fatalError() + #endif case .directionalLightShadow: - let component = component as! DirectionalLightComponent.Shadow - self.properties = .directionalLightShadow( - DirectionalLightShadowComponentProperties( - depthBias: component.depthBias, - maximumDistance: component.maximumDistance + #if os(iOS) && !os(xrOS) + let component = component as! DirectionalLightComponent.Shadow + self.properties = .directionalLightShadow( + DirectionalLightShadowComponentProperties( + depthBias: component.depthBias, + maximumDistance: component.maximumDistance + ) ) - ) + #else + //FIXME: xrOS compatibility + fatalError() + #endif case .model: let component = component as! ModelComponent @@ -147,24 +156,34 @@ public struct IdentifiableComponent: Codable { ) case .pointLight: - let component = component as! PointLightComponent - self.properties = .pointLight( - PointLightComponentProperties( - intensity: component.intensity, - attenuationRadius: component.attenuationRadius + #if os(iOS) && !os(xrOS) + let component = component as! PointLightComponent + self.properties = .pointLight( + PointLightComponentProperties( + intensity: component.intensity, + attenuationRadius: component.attenuationRadius + ) ) - ) + #else + //FIXME: xrOS compatibility + fatalError() + #endif case .spotLight: - let component = component as! SpotLightComponent - self.properties = .spotLight( - SpotLightComponentProperties( - intensity: component.intensity, - innerAngleInDegrees: component.innerAngleInDegrees, - outerAngleInDegrees: component.outerAngleInDegrees, - attenuationRadius: component.attenuationRadius + #if os(iOS) && !os(xrOS) + let component = component as! SpotLightComponent + self.properties = .spotLight( + SpotLightComponentProperties( + intensity: component.intensity, + innerAngleInDegrees: component.innerAngleInDegrees, + outerAngleInDegrees: component.outerAngleInDegrees, + attenuationRadius: component.attenuationRadius + ) ) - ) + #else + //FIXME: xrOS compatibility + fatalError() + #endif case .spotLightShadow: // As of RealityKit 2.0, it was empty. @@ -209,70 +228,71 @@ extension IdentifiableComponent: Equatable, Hashable { } } -extension IdentifiableComponent: CustomDumpReflectable { - public var customDumpMirror: Mirror { - .init( - self.properties, - children: [ - "componentType": self.componentType, - "properties": self.properties, - ], - displayStyle: .struct - ) - } -} - -extension IdentifiableComponent: CustomDumpRepresentable { - public var customDumpValue: Any { - switch self.properties { - case .anchoring(let value): - return value - - case .characterController(let value): - return value - - case .characterControllerState(let value): - return value - - case .collision(let value): - return value - - case .directionalLight(let value): - return value - - case .directionalLightShadow(let value): - return value - - case .model(let value): - return value - - case .modelDebugOptions(let value): - return value - - case .perspectiveCamera(let value): - return value - - case .physicsBody(let value): - return value - - case .physicsMotion(let value): - return value - - case .pointLight(let value): - return value - - case .spotLight(let value): - return value - - case .spotLightShadow(let value): - return value - - case .synchronization(let value): - return value - - case .transform(let value): - return value - - } - } -} +//FIXME: +//extension IdentifiableComponent: CustomDumpReflectable { +// public var customDumpMirror: Mirror { +// .init( +// self.properties, +// children: [ +// "componentType": self.componentType, +// "properties": self.properties, +// ], +// displayStyle: .struct +// ) +// } +//} + +//extension IdentifiableComponent: CustomDumpRepresentable { +// public var customDumpValue: Any { +// switch self.properties { +// case .anchoring(let value): +// return value +// +// case .characterController(let value): +// return value +// +// case .characterControllerState(let value): +// return value +// +// case .collision(let value): +// return value +// +// case .directionalLight(let value): +// return value +// +// case .directionalLightShadow(let value): +// return value +// +// case .model(let value): +// return value +// +// case .modelDebugOptions(let value): +// return value +// +// case .perspectiveCamera(let value): +// return value +// +// case .physicsBody(let value): +// return value +// +// case .physicsMotion(let value): +// return value +// +// case .pointLight(let value): +// return value +// +// case .spotLight(let value): +// return value +// +// case .spotLightShadow(let value): +// return value +// +// case .synchronization(let value): +// return value +// +// case .transform(let value): +// return value +// +// } +// } +//} diff --git a/Sources/Models/Component/Properties/AnchoringComponentProperties.swift b/Sources/Models/Component/Properties/AnchoringComponentProperties.swift index d13c251..64e8874 100644 --- a/Sources/Models/Component/Properties/AnchoringComponentProperties.swift +++ b/Sources/Models/Component/Properties/AnchoringComponentProperties.swift @@ -1,4 +1,3 @@ -import CustomDump import Foundation import RealityKit @@ -50,14 +49,15 @@ public enum CodableTarget: Codable { } } -extension AnchoringComponentProperties: CustomDumpReflectable { - public var customDumpMirror: Mirror { - .init( - self, - children: [ - "target": self.target - ], - displayStyle: .struct - ) - } -} +//FIXME: +//extension AnchoringComponentProperties: CustomDumpReflectable { +// public var customDumpMirror: Mirror { +// .init( +// self, +// children: [ +// "target": self.target +// ], +// displayStyle: .struct +// ) +// } +//} diff --git a/Sources/Models/Decoder.swift b/Sources/Models/Decoder.swift new file mode 100644 index 0000000..7abcb85 --- /dev/null +++ b/Sources/Models/Decoder.swift @@ -0,0 +1,22 @@ +import Foundation + +public var defaultEncoder: JSONEncoder = { + let encoder = JSONEncoder() + encoder.nonConformingFloatEncodingStrategy = .convertToString( + positiveInfinity: "INF", + negativeInfinity: "-INF", + nan: "NAN" + ) + encoder.outputFormatting = .prettyPrinted + return encoder +}() + +public var defaultDecoder: JSONDecoder = { + let decoder = JSONDecoder() + decoder.nonConformingFloatDecodingStrategy = .convertFromString( + positiveInfinity: "INF", + negativeInfinity: "-INF", + nan: "NAN" + ) + return decoder +}() diff --git a/Sources/Models/Entity+Find.swift b/Sources/Models/Entity+Find.swift new file mode 100644 index 0000000..00044cf --- /dev/null +++ b/Sources/Models/Entity+Find.swift @@ -0,0 +1,27 @@ +import Foundation +import RealityKit + +/// Performs a depth-first search in a tree-like structure represented by `Entity`. +/// +/// - Parameters: +/// - root: The root node of the tree. +/// - targetID: The target ID to search for. +/// - Returns: The `Entity` with the matching ID, or `nil` if not found. +/// +/// - Complexity: O(n), where n is the number of nodes in the tree. +/// +/// - Note: This method assumes that each node has a unique ID. +/// +public func findEntity(root: Entity, targetID: UInt64) -> Entity? { + if root.id == targetID { + return root + } + + for child in root.children { + if let foundNode = findEntity(root: child, targetID: targetID) { + return foundNode + } + } + + return nil +} diff --git a/Sources/Models/EntitySelection.swift b/Sources/Models/EntitySelection.swift new file mode 100644 index 0000000..c61742c --- /dev/null +++ b/Sources/Models/EntitySelection.swift @@ -0,0 +1,11 @@ +import Foundation + +public struct EntitySelection: Codable { + public let entityID: UInt64 + + public init( + _ entityID: UInt64 + ) { + self.entityID = entityID + } +} diff --git a/Sources/Models/Component/IdentifiableComponent+Find.swift b/Sources/Models/IdentifiableEntity+Find.swift similarity index 76% rename from Sources/Models/Component/IdentifiableComponent+Find.swift rename to Sources/Models/IdentifiableEntity+Find.swift index 5b2a4a7..5b012a1 100644 --- a/Sources/Models/Component/IdentifiableComponent+Find.swift +++ b/Sources/Models/IdentifiableEntity+Find.swift @@ -11,14 +11,14 @@ import Foundation /// /// - Note: This method assumes that each node has a unique ID. /// -public func findEntity(root: IdentifiableEntity, targetID: UInt64) -> IdentifiableEntity? { +public func findIdentifiableEntity(root: IdentifiableEntity, targetID: UInt64) -> IdentifiableEntity? { if root.id == targetID { return root } if let children = root.children { for child in children { - if let foundNode = findEntity(root: child, targetID: targetID) { + if let foundNode = findIdentifiableEntity(root: child, targetID: targetID) { return foundNode } } diff --git a/Sources/Models/IdentifiableEntity.swift b/Sources/Models/IdentifiableEntity.swift index 37e0bbc..f260336 100644 --- a/Sources/Models/IdentifiableEntity.swift +++ b/Sources/Models/IdentifiableEntity.swift @@ -1,4 +1,3 @@ -import CustomDump import Foundation import RealityKit @@ -59,16 +58,16 @@ public struct IdentifiableEntity: Equatable, Identifiable, Hashable, Codable { public struct Hierarhy: Codable { - public let hasParent: Bool + public let parentID: UInt64? public var childrenCount: Int { children.count } public var children: [IdentifiableEntity] public init( children: [IdentifiableEntity], - hasParent: Bool + parentID: UInt64? ) { self.children = children - self.hasParent = hasParent + self.parentID = parentID } } @@ -89,9 +88,11 @@ public struct IdentifiableEntity: Equatable, Identifiable, Hashable, Codable { hierarhy: IdentifiableEntity.Hierarhy, components: IdentifiableEntity.Components ) { - if let anchor = entity as? AnchorEntity { - self.anchorIdentifier = anchor.anchorIdentifier - } + #if !os(visionOS) + if let anchor = entity as? AnchorEntity { + self.anchorIdentifier = anchor.anchorIdentifier + } + #endif self.id = entity.id self.isAccessibilityElement = entity.isAccessibilityElement self.accessibilityLabel = entity.accessibilityLabel @@ -112,18 +113,34 @@ extension IdentifiableEntity.EntityType: RawRepresentable { return AnchorEntity.self // case .bodyTrackedEntity: // return AnchorEntity.self + case .directionalLight: - return DirectionalLight.self + #if !os(visionOS) + return DirectionalLight.self + #endif + fatalError() + case .entity: return Entity.self + case .model: return ModelEntity.self + case .perspectiveCamera: return PerspectiveCamera.self + case .pointLight: - return PointLight.self + #if !os(visionOS) + return PointLight.self + #endif + fatalError() + case .spotLight: - return SpotLight.self + #if !os(visionOS) + return SpotLight.self + #endif + fatalError() + case .triggerVolume: return TriggerVolume.self } @@ -239,77 +256,3 @@ extension IdentifiableEntity.EntityType { } } } - -extension IdentifiableEntity.State: CustomDumpReflectable { - public var customDumpMirror: Mirror { - .init( - self, - children: [ - "isEnabled": self.isEnabled, - "isEnabledInHierarchy": self.isEnabledInHierarchy, - "isActive": self.isActive, - "isAnchored": self.isAnchored, - ], - displayStyle: .dictionary - ) - } -} - -extension IdentifiableEntity.Hierarhy: CustomDumpReflectable { - public var customDumpMirror: Mirror { - .init( - self, - children: [ - "hasParent": self.hasParent, - "children": self.children, - ], - displayStyle: .dictionary - ) - } -} - -extension IdentifiableEntity.Components: CustomDumpReflectable { - public var customDumpMirror: Mirror { - .init( - self, - children: [ - "_items": self.components - ], - displayStyle: .collection - ) - } -} - -//extension IdentifiableEntity: CustomDumpStringConvertible { -// public var customDumpDescription: String { -// """ -// Anchor( -// id: \(self.id), -// components: \(String(customDumping: self.components)) -// -// ) -// """ -// } -//} - -extension IdentifiableEntity: CustomDumpReflectable { - public var customDumpMirror: Mirror { - .init( - self, - children: [ - "id": self.id, - "isAccessibilityElement": self.isAccessibilityElement, - "accessibilityLabel": self.accessibilityLabel, - "accessibilityDescription": self.accessibilityDescription, - "anchorIdentifier": self.anchorIdentifier, - "availableAnimations": self.availableAnimations, - "name": self.name, - "entityType": self.entityType, - "state": self.state, - "hierarhy": self.hierarhy, - "components": self.components.components, - ], - displayStyle: .struct - ) - } -} diff --git a/Sources/Models/_DebugOptions.swift b/Sources/Models/_DebugOptions.swift index a9a14d3..b98399e 100644 --- a/Sources/Models/_DebugOptions.swift +++ b/Sources/Models/_DebugOptions.swift @@ -1,6 +1,6 @@ import Foundation -//Needed because macOS doesn't have all the values available. +// It's necessary because macOS doesn't have all the options available. public struct _DebugOptions: OptionSet, Codable, Equatable { public let rawValue: Int diff --git a/Sources/MultipeerClient/Live.swift b/Sources/MultipeerClient/Live.swift index 279211c..1ee9ad1 100644 --- a/Sources/MultipeerClient/Live.swift +++ b/Sources/MultipeerClient/Live.swift @@ -9,7 +9,7 @@ extension MultipeerClient: DependencyKey { if let peerName { name = peerName } else { - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) name = await UIDevice.current.name #elseif os(macOS) name = Host.current().name ?? UUID().uuidString @@ -93,7 +93,6 @@ extension MultipeerClient { ) -> AsyncStream { AsyncStream { continuation in let myPeerID = MCPeerID(displayName: peerName) - setupSession() switch sessionType { @@ -195,7 +194,8 @@ extension MultipeerClient { } } catch { //TODO: handle errors - fatalError("Failed to send data.") + //fatalError("Failed to send data.") + print("Failed to send data.") } } } diff --git a/Sources/MultipeerClient/Resources/Mock/not_so_simple_arview.json b/Sources/MultipeerClient/Resources/Mock/not_so_simple_arview.json index e95497f..94fa2b2 100644 --- a/Sources/MultipeerClient/Resources/Mock/not_so_simple_arview.json +++ b/Sources/MultipeerClient/Resources/Mock/not_so_simple_arview.json @@ -2,42 +2,304 @@ "scene" : { "anchors" : [ { + "components" : { + "components" : [ + { + "properties" : { + "anchoring" : { + "_0" : { + "target" : { + "plane" : { + "classifications" : [ + 18446744073709551615 + ], + "_0" : [ + 1 + ], + "minimumBounds" : [ + 0, + 0 + ] + } + } + } + } + }, + "componentType" : { + "anchoring" : { + + } + } + }, + { + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "ownershipTransferMode" : "autoAccept", + "identifier" : 8804710806275880779 + } + } + }, + "componentType" : { + "synchronization" : { + + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1 + ] + ], + "scale" : [ + 1, + 1, + 1 + ], + "translation" : [ + 0, + 0, + 0 + ] + } + } + } + } + ] + }, "name" : "", - "id" : 6102830334754037284, "isAccessibilityElement" : false, + "state" : { + "isAnchored" : false, + "isActive" : false, + "isEnabledInHierarchy" : true, + "isEnabled" : true + }, + "entityType" : { + "entity" : { + + } + }, + "availableAnimations" : [ + + ], "hierarhy" : { "children" : [ { - "name" : "", - "id" : 7073299286984424488, + "id" : 651673476834153011, "isAccessibilityElement" : false, + "state" : { + "isEnabled" : true, + "isAnchored" : false, + "isEnabledInHierarchy" : true, + "isActive" : false + }, + "name" : "", + "availableAnimations" : [ + + ], + "entityType" : { + "anchor" : { + + } + }, + "components" : { + "components" : [ + { + "properties" : { + "anchoring" : { + "_0" : { + "target" : { + "plane" : { + "minimumBounds" : [ + 0, + 0 + ], + "_0" : [ + 1 + ], + "classifications" : [ + 18446744073709551615 + ] + } + } + } + } + }, + "componentType" : { + "anchoring" : { + + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "ownershipTransferMode" : "autoAccept", + "identifier" : 7499355142807531241 + } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1 + ] + ], + "translation" : [ + 0, + 0, + 0 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "scale" : [ + 1, + 1, + 1 + ] + } + } + } + } + ] + }, "hierarhy" : { + "parentID" : 12039990845665541814, "children" : [ { - "name" : "", - "id" : 14572550037717354582, + "state" : { + "isActive" : false, + "isAnchored" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, "isAccessibilityElement" : false, + "availableAnimations" : [ + + ], + "id" : 15302631343601957711, "hierarhy" : { "children" : [ { - "name" : "Steel Box", - "id" : 4176345584920859981, + "entityType" : { + "entity" : { + + } + }, "isAccessibilityElement" : false, + "availableAnimations" : [ + + ], + "state" : { + "isAnchored" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isActive" : false + }, + "name" : "Steel Box", "hierarhy" : { + "parentID" : 15302631343601957711, "children" : [ { - "name" : "simpBld_root", - "id" : 7194474703674396084, - "isAccessibilityElement" : false, + "id" : 12405626522203503055, + "entityType" : { + "entity" : { + + } + }, "hierarhy" : { + "parentID" : 18133920105240902742, "children" : [ - ], - "hasParent" : true + ] }, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { @@ -46,38 +308,33 @@ } ], + "boundsMargin" : 0, "mesh" : { "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.05000000074505806, - -0.05000000074505806, - -0.05000000074505806 + -0.05, + -0.05, + -0.05 ], "max" : [ - 0.05000000074505806, - 0.05000000074505806, - 0.05000000074505806 + 0.05, + 0.05, + 0.05 ] } - }, - "boundsMargin" : 0 + } } } - }, - "componentType" : { - "model" : { - - } } }, { "properties" : { "synchronization" : { "_0" : { + "identifier" : 1040248403246073352, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 11428503663065308588 + "ownershipTransferMode" : "autoAccept" } } }, @@ -88,20 +345,14 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -128,59 +379,67 @@ 1 ] ], + "translation" : [ + 0, + 0, + 0 + ], "scale" : [ 1, 1, 1 + ], + "rotation" : [ + 0, + 0, + 0, + 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], + "isAccessibilityElement" : false, "state" : { + "isAnchored" : false, "isActive" : false, "isEnabledInHierarchy" : true, - "isAnchored" : false, "isEnabled" : true - } + }, + "availableAnimations" : [ + + ], + "name" : "simpBld_root" } - ], - "hasParent" : true + ] }, + "id" : 18133920105240902742, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, + "identifier" : 1208626014608241259, "ownershipTransferMode" : "autoAccept", - "identifier" : 2638832547936298595 + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { @@ -190,11 +449,6 @@ 0, 1 ], - "translation" : [ - 0, - 0.05000000074505806, - 0 - ], "matrix" : [ [ 1, @@ -216,7 +470,7 @@ ], [ 0, - 0.05000000074505806, + 0.05, 0, 1 ] @@ -225,69 +479,59 @@ 1, 1, 1 + ], + "translation" : [ + 0, + 0.05, + 0 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } ], - "hasParent" : true + "parentID" : 651673476834153011 + }, + "entityType" : { + "entity" : { + + } }, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 11065276484325783532 + "identifier" : 15064199269789291320, + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -300,7 +544,6 @@ 1, 0, 0 - ], [ 0, @@ -315,50 +558,43 @@ 1 ] ], - "scale" : [ - 1, - 1, + "translation" : [ + 0, + 0, + 0 + ], + "rotation" : [ + 0, + 0, + 0, 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } + "name" : "" + }, + { + "state" : { + "isEnabledInHierarchy" : true, + "isActive" : false, + "isEnabled" : true, + "isAnchored" : false }, "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "Ground Plane", - "id" : 5087790995183636815, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true - }, "components" : { "components" : [ { + "componentType" : { + "collision" : { + + } + }, "properties" : { "collision" : { "_0" : { @@ -367,59 +603,54 @@ } ], - "mode" : "default", "filter" : { "group" : 1, "mask" : 1 - } + }, + "mode" : "default" } } - }, - "componentType" : { - "collision" : { - - } } }, { + "componentType" : { + "physicsBody" : { + + } + }, "properties" : { "physicsBody" : { "_0" : { - "isTranslationLocked" : { - "x" : false, - "y" : false, - "z" : false - }, "isContinuousCollisionDetectionEnabled" : false, + "mode" : "static", "isRotationLocked" : { "x" : false, - "y" : false, - "z" : false + "z" : false, + "y" : false }, - "mode" : "static" + "isTranslationLocked" : { + "z" : false, + "x" : false, + "y" : false + } } } - }, - "componentType" : { - "physicsBody" : { - - } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, + "identifier" : 16000888938473210839, "ownershipTransferMode" : "autoAccept", - "identifier" : 15155331625253781787 + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { @@ -432,11 +663,6 @@ 0, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -463,6 +689,11 @@ 1 ] ], + "translation" : [ + 0, + 0, + 0 + ], "scale" : [ 1, 1, @@ -479,52 +710,1043 @@ } ] }, + "id" : 17269835022948775294, "entityType" : { "entity" : { } }, - "availableAnimations" : [ + "isAccessibilityElement" : false, + "hierarhy" : { + "children" : [ - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + ], + "parentID" : 651673476834153011 + }, + "name" : "Ground Plane" } - ], - "hasParent" : true + ] + } + }, + { + "hierarhy" : { + "parentID" : 12039990845665541814, + "children" : [ + + ] + }, + "name" : "Le Box", + "isAccessibilityElement" : false, + "id" : 4103498616501043870, + "state" : { + "isEnabledInHierarchy" : true, + "isActive" : false, + "isAnchored" : false, + "isEnabled" : true + }, + "availableAnimations" : [ + + ], + "entityType" : { + "entity" : { + + } }, "components" : { "components" : [ { - "properties" : { - "anchoring" : { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { "_0" : { - "target" : { - "plane" : [ + "boundsMargin" : 0, + "mesh" : { + "bounds" : { + "max" : [ + 1.012114, + 0.5, + 0.5107093 + ], + "min" : [ + -1.012114, + -0.5, + -0.5107093 + ] + }, + "expectedMaterialCount" : 1 + }, + "materials" : [ + { + + } + ] + } + } + } + }, + { + "properties" : { + "modelDebugOptions" : { + "_0" : { + "visualizationMode" : "DerivedNormal" + } + } + }, + "componentType" : { + "modelDebugOptions" : { + + } + } + }, + { + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "identifier" : 7595607321188015836, + "ownershipTransferMode" : "autoAccept" + } + } + }, + "componentType" : { + "synchronization" : { + + } + } + }, + { + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0.5331265, + 0, + 0.8460356 + ], + "scale" : [ + 0.99999994, + 0.99999994, + 0.99999994 + ], + "matrix" : [ + [ + 0.43155238, + 0, + -0.90208787, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0.90208787, + 0, + 0.4315524, + 0 + ], + [ + 0.0066464543, + 0.5, + -4.5539722, + 1 + ] + ], + "translation" : [ + 0.0066464543, + 0.5, + -4.5539722 + ] + } + } + }, + "componentType" : { + "transform" : { + + } + } + } + ] + } + } + ] + }, + "id" : 12039990845665541814 + }, + { + "isAccessibilityElement" : false, + "availableAnimations" : [ + + ], + "anchorIdentifier" : "8ED49F84-E830-491F-A16A-A9D1649FAC35", + "name" : "Le Anchor", + "id" : 14858563986960262752, + "hierarhy" : { + "children" : [ + { + "availableAnimations" : [ + + ], + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "mesh" : { + "bounds" : { + "min" : [ + -0.06435564, + -0.06435564, + -0.06435564 + ], + "max" : [ + 0.06435564, + 0.06435564, + 0.06435564 + ] + }, + "expectedMaterialCount" : 1 + }, + "materials" : [ + { + + } + ], + "boundsMargin" : 0 + } + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "identifier" : 2780289721360782784, + "ownershipTransferMode" : "autoAccept", + "isOwner" : true + } + } + } + }, + { + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "translation" : [ + -0.10744989, + 0.11504829, + 0.60147583 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, 0 + ], + [ + -0.10744989, + 0.11504829, + 0.60147583, + 1 ] + ], + "scale" : [ + 1, + 1, + 1 + ] + } + } + }, + "componentType" : { + "transform" : { + + } + } + } + ] + }, + "state" : { + "isActive" : true, + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isAnchored" : true + }, + "id" : 2412354501491433252, + "name" : "", + "entityType" : { + "entity" : { + + } + }, + "hierarhy" : { + "children" : [ + + ], + "parentID" : 14858563986960262752 + }, + "isAccessibilityElement" : false + }, + { + "entityType" : { + "entity" : { + + } + }, + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "materials" : [ + { + + } + ], + "boundsMargin" : 0, + "mesh" : { + "expectedMaterialCount" : 1, + "bounds" : { + "max" : [ + 0.0506111, + 0.0506111, + 0.0506111 + ], + "min" : [ + -0.0506111, + -0.0506111, + -0.0506111 + ] + } } } } - }, - "componentType" : { - "anchoring" : { - - } } }, { "properties" : { "synchronization" : { "_0" : { + "identifier" : 7530465550901577790, + "isOwner" : true, + "ownershipTransferMode" : "autoAccept" + } + } + }, + "componentType" : { + "synchronization" : { + + } + } + }, + { + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "scale" : [ + 1, + 1, + 1 + ], + "translation" : [ + -0.46181834, + 0.7725004, + -0.792833 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + -0.46181834, + 0.7725004, + -0.792833, + 1 + ] + ] + } + } + }, + "componentType" : { + "transform" : { + + } + } + } + ] + }, + "id" : 13160741885674332701, + "hierarhy" : { + "children" : [ + + ], + "parentID" : 14858563986960262752 + }, + "name" : "", + "isAccessibilityElement" : false, + "state" : { + "isEnabled" : true, + "isActive" : true, + "isAnchored" : true, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + + ] + }, + { + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + }, + "entityType" : { + "entity" : { + + } + }, + "id" : 8256126316089268502, + "isAccessibilityElement" : false, + "components" : { + "components" : [ + { + "properties" : { + "model" : { + "_0" : { + "mesh" : { + "expectedMaterialCount" : 1, + "bounds" : { + "max" : [ + 0.0621462, + 0.0621462, + 0.0621462 + ], + "min" : [ + -0.0621462, + -0.0621462, + -0.0621462 + ] + } + }, + "boundsMargin" : 0, + "materials" : [ + { + + } + ] + } + } + }, + "componentType" : { + "model" : { + + } + } + }, + { + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "identifier" : 8800700955716479800, + "ownershipTransferMode" : "autoAccept" + } + } + }, + "componentType" : { + "synchronization" : { + + } + } + }, + { + "properties" : { + "transform" : { + "_0" : { + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + -0.80048203, + 0.5589207, + 0.90110385, + 1 + ] + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "scale" : [ + 1, + 1, + 1 + ], + "translation" : [ + -0.80048203, + 0.5589207, + 0.90110385 + ] + } + } + }, + "componentType" : { + "transform" : { + + } + } + } + ] + }, + "state" : { + "isAnchored" : true, + "isActive" : true, + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + + ], + "name" : "" + }, + { + "name" : "", + "components" : { + "components" : [ + { + "properties" : { + "model" : { + "_0" : { + "mesh" : { + "bounds" : { + "min" : [ + -0.06187874, + -0.06187874, + -0.06187874 + ], + "max" : [ + 0.06187874, + 0.06187874, + 0.06187874 + ] + }, + "expectedMaterialCount" : 1 + }, + "materials" : [ + { + + } + ], + "boundsMargin" : 0 + } + } + }, + "componentType" : { + "model" : { + + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "ownershipTransferMode" : "autoAccept", + "identifier" : 16107654173314287590 + } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "scale" : [ + 1, + 1, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0.08737457, + 0.1767258, + -0.016834378, + 1 + ] + ], + "translation" : [ + 0.08737457, + 0.1767258, + -0.016834378 + ] + } + } + } + } + ] + }, + "isAccessibilityElement" : false, + "state" : { + "isActive" : true, + "isAnchored" : true, + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + + ], + "entityType" : { + "entity" : { + + } + }, + "id" : 17538366419468802031, + "hierarhy" : { + "children" : [ + + ], + "parentID" : 14858563986960262752 + } + }, + { + "isAccessibilityElement" : false, + "availableAnimations" : [ + + ], + "id" : 14204025177297098107, + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + }, + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "mesh" : { + "bounds" : { + "max" : [ + 0.04794494, + 0.04794494, + 0.04794494 + ], + "min" : [ + -0.04794494, + -0.04794494, + -0.04794494 + ] + }, + "expectedMaterialCount" : 1 + }, + "boundsMargin" : 0, + "materials" : [ + { + + } + ] + } + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "identifier" : 7264725841809340913, + "isOwner" : true, + "ownershipTransferMode" : "autoAccept" + } + } + } + }, + { + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0.105076194, + 0.86111957, + 0.9259379, + 1 + ] + ], + "scale" : [ + 1, + 1, + 1 + ], + "translation" : [ + 0.105076194, + 0.86111957, + 0.9259379 + ] + } + } + }, + "componentType" : { + "transform" : { + + } + } + } + ] + }, + "name" : "", + "entityType" : { + "entity" : { + + } + }, + "state" : { + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isAnchored" : true, + "isActive" : true + } + }, + { + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + }, + "entityType" : { + "entity" : { + + } + }, + "isAccessibilityElement" : false, + "name" : "", + "availableAnimations" : [ + + ], + "state" : { + "isAnchored" : true, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isActive" : true + }, + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "mesh" : { + "bounds" : { + "min" : [ + -0.05664925, + -0.05664925, + -0.05664925 + ], + "max" : [ + 0.05664925, + 0.05664925, + 0.05664925 + ] + }, + "expectedMaterialCount" : 1 + }, + "materials" : [ + { + + } + ], + "boundsMargin" : 0 + } + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "identifier" : 6907467823057996719, + "ownershipTransferMode" : "autoAccept" + } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "scale" : [ + 1, + 1, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0.08666754, + 0.44318128, + 0.2775737, + 1 + ] + ], + "translation" : [ + 0.08666754, + 0.44318128, + 0.2775737 + ] + } + } + } + } + ] + }, + "id" : 8028177140811111093 + }, + { + "availableAnimations" : [ + + ], + "state" : { + "isEnabledInHierarchy" : true, + "isActive" : true, + "isEnabled" : true, + "isAnchored" : true + }, + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "materials" : [ + { + + } + ], + "mesh" : { + "bounds" : { + "max" : [ + 0.09726049, + 0.09726049, + 0.09726049 + ], + "min" : [ + -0.09726049, + -0.09726049, + -0.09726049 + ] + }, + "expectedMaterialCount" : 1 + }, + "boundsMargin" : 0 + } + } + } + }, + { + "properties" : { + "synchronization" : { + "_0" : { + "identifier" : 18203743619633878141, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 6300477955791830780 + "ownershipTransferMode" : "autoAccept" } } }, @@ -538,17 +1760,22 @@ "properties" : { "transform" : { "_0" : { + "translation" : [ + 0.9215001, + 0.032578886, + -0.24276376 + ], + "scale" : [ + 1, + 1, + 1 + ], "rotation" : [ 0, 0, 0, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -569,16 +1796,11 @@ 0 ], [ - 0, - 0, - 0, + 0.9215001, + 0.032578886, + -0.24276376, 1 ] - ], - "scale" : [ - 1, - 1, - 1 ] } } @@ -591,30 +1813,296 @@ } ] }, + "name" : "", + "isAccessibilityElement" : false, "entityType" : { - "anchor" : { + "entity" : { + + } + }, + "id" : 16506815809766514615, + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + } + }, + { + "id" : 6013102347319719841, + "entityType" : { + "entity" : { } }, + "name" : "", + "state" : { + "isActive" : true, + "isAnchored" : true, + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + + ], + "isAccessibilityElement" : false, + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + }, + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "mesh" : { + "bounds" : { + "min" : [ + -0.074658826, + -0.074658826, + -0.074658826 + ], + "max" : [ + 0.074658826, + 0.074658826, + 0.074658826 + ] + }, + "expectedMaterialCount" : 1 + }, + "materials" : [ + { + + } + ], + "boundsMargin" : 0 + } + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "ownershipTransferMode" : "autoAccept", + "isOwner" : true, + "identifier" : 3435488670034746087 + } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "translation" : [ + -0.7260494, + 0.26228762, + -0.2526797 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "scale" : [ + 1, + 1, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + -0.7260494, + 0.26228762, + -0.2526797, + 1 + ] + ] + } + } + } + } + ] + } + }, + { + "isAccessibilityElement" : false, + "name" : "", "availableAnimations" : [ ], "state" : { - "isActive" : false, + "isEnabled" : true, "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + "isAnchored" : true, + "isActive" : true + }, + "hierarhy" : { + "children" : [ + + ], + "parentID" : 14858563986960262752 + }, + "components" : { + "components" : [ + { + "componentType" : { + "model" : { + + } + }, + "properties" : { + "model" : { + "_0" : { + "boundsMargin" : 0, + "mesh" : { + "expectedMaterialCount" : 1, + "bounds" : { + "min" : [ + -0.08314078, + -0.08314078, + -0.08314078 + ], + "max" : [ + 0.08314078, + 0.08314078, + 0.08314078 + ] + } + }, + "materials" : [ + { + + } + ] + } + } + } + }, + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "identifier" : 2313600920184178338, + "ownershipTransferMode" : "autoAccept", + "isOwner" : true + } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "translation" : [ + -0.86410046, + 0.29503608, + -0.86886096 + ], + "scale" : [ + 1, + 1, + 1 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + -0.86410046, + 0.29503608, + -0.86886096, + 1 + ] + ] + } + } + } + } + ] + }, + "entityType" : { + "entity" : { + + } + }, + "id" : 10997791758721849919 }, { - "name" : "Le Box", - "id" : 16532086729429668694, - "isAccessibilityElement" : false, + "id" : 2267383681041927561, "hierarhy" : { + "parentID" : 14858563986960262752, "children" : [ - ], - "hasParent" : true + ] }, "components" : { "components" : [ @@ -627,22 +2115,22 @@ } ], + "boundsMargin" : 0, "mesh" : { "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.88825875520706177, - -0.5, - -0.3694419264793396 + -0.04297827, + -0.04297827, + -0.04297827 ], "max" : [ - 0.88825875520706177, - 0.5, - 0.3694419264793396 + 0.04297827, + 0.04297827, + 0.04297827 ] } - }, - "boundsMargin" : 0 + } } } }, @@ -652,27 +2140,13 @@ } } }, - { - "properties" : { - "modelDebugOptions" : { - "_0" : { - "visualizationMode" : "DerivedNormal" - } - } - }, - "componentType" : { - "modelDebugOptions" : { - - } - } - }, { "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 10896632844503232826 + "identifier" : 12939072837768264525 } } }, @@ -686,47 +2160,47 @@ "properties" : { "transform" : { "_0" : { + "translation" : [ + -0.96248937, + 0.39725435, + -0.6239518 + ], + "scale" : [ + 1, + 1, + 1 + ], "rotation" : [ 0, - -0.6255650520324707, 0, - 0.78017204999923706 - ], - "translation" : [ - 0.13221091032028198, - 0.5, - -4.3180141448974609 + 0, + 1 ], "matrix" : [ [ - 0.21733668446540833, + 1, + 0, 0, - 0.97609663009643555, 0 ], [ - -0, + 0, 1, 0, 0 ], [ - -0.97609663009643555, - -0, - 0.21733668446540833, + 0, + 0, + 1, 0 ], [ - 0.13221091032028198, - 0.5, - -4.3180141448974609, + -0.96248937, + 0.39725435, + -0.6239518, 1 ] - ], - "scale" : [ - 0.99999988079071045, - 1, - 0.99999988079071045 ] } } @@ -739,182 +2213,66 @@ } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], + "isAccessibilityElement" : false, "state" : { - "isActive" : false, "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - } - ], - "hasParent" : false - }, - "components" : { - "components" : [ - { - "properties" : { - "anchoring" : { - "_0" : { - "target" : { - "plane" : [ - 0, - 0 - ] - } - } - } + "isEnabled" : true, + "isActive" : true, + "isAnchored" : true }, - "componentType" : { - "anchoring" : { + "name" : "", + "entityType" : { + "entity" : { } - } - }, - { - "properties" : { - "synchronization" : { - "_0" : { - "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 15607615438144295092 - } - } }, - "componentType" : { - "synchronization" : { + "availableAnimations" : [ - } - } + ] }, { - "properties" : { - "transform" : { - "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], - "matrix" : [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - 0, - 1 - ] - ], - "scale" : [ - 1, - 1, - 1 - ] - } - } - }, - "componentType" : { - "transform" : { - - } - } - } - ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true - }, - "isAccessibilityElement" : false, - "id" : 11708666581627456330, - "anchorIdentifier" : "5291ABF4-ED06-4A65-8853-A3DAACF708C0", - "hierarhy" : { - "children" : [ - { - "name" : "", - "id" : 5874808153791026085, - "isAccessibilityElement" : false, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 14858563986960262752 + }, + "state" : { + "isAnchored" : true, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isActive" : true }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], + "boundsMargin" : 0, "mesh" : { - "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.07981930673122406, - -0.07981930673122406, - -0.07981930673122406 + -0.061701767, + -0.061701767, + -0.061701767 ], "max" : [ - 0.07981930673122406, - 0.07981930673122406, - 0.07981930673122406 + 0.061701767, + 0.061701767, + 0.061701767 ] - } + }, + "expectedMaterialCount" : 1 }, - "boundsMargin" : 0 + "materials" : [ + { + + } + ] } } }, @@ -925,35 +2283,34 @@ } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 3667823477816065105 + "identifier" : 7813774532349695560 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ - 0.73615801334381104, - 0.50801986455917358, - -0.06983482837677002 + 0.49924505, + 0.74230313, + 0.77110696 ], "matrix" : [ [ @@ -975,9 +2332,9 @@ 0 ], [ - 0.73615801334381104, - 0.50801986455917358, - -0.06983482837677002, + 0.49924505, + 0.74230313, + 0.77110696, 1 ] ], @@ -985,14 +2342,15 @@ 1, 1, 1 + ], + "rotation" : [ + 0, + 0, + 0, + 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] @@ -1002,32 +2360,38 @@ } }, + "isAccessibilityElement" : false, + "name" : "", + "id" : 1566251445808726210 + }, + { "availableAnimations" : [ ], - "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true - } - }, - { - "name" : "", - "id" : 12106713724682859916, - "isAccessibilityElement" : false, + "entityType" : { + "entity" : { + + } + }, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 14858563986960262752 }, + "id" : 11645285245526926156, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { + "boundsMargin" : 0, "materials" : [ { @@ -1037,58 +2401,62 @@ "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.09513130784034729, - -0.09513130784034729, - -0.09513130784034729 + -0.07863322, + -0.07863322, + -0.07863322 ], "max" : [ - 0.09513130784034729, - 0.09513130784034729, - 0.09513130784034729 + 0.07863322, + 0.07863322, + 0.07863322 ] } - }, - "boundsMargin" : 0 + } } } - }, - "componentType" : { - "model" : { - - } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 4357603380106690503 + "isOwner" : true, + "identifier" : 5233246691438526092 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { + "translation" : [ + -0.63003945, + 0.8781037, + 0.87562084 + ], + "scale" : [ + 1, + 1, + 1 + ], "rotation" : [ 0, 0, 0, 1 ], - "translation" : [ - -0.66091656684875488, - 0.58013761043548584, - 0.74225318431854248 - ], "matrix" : [ [ 1, @@ -1109,87 +2477,76 @@ 0 ], [ - -0.66091656684875488, - 0.58013761043548584, - 0.74225318431854248, + -0.63003945, + 0.8781037, + 0.87562084, 1 ] - ], - "scale" : [ - 1, - 1, - 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "isAccessibilityElement" : false, + "name" : "", + "state" : { + "isActive" : true, + "isEnabled" : true, + "isAnchored" : true, + "isEnabledInHierarchy" : true + } + }, + { "entityType" : { "entity" : { } }, - "availableAnimations" : [ - - ], "state" : { "isActive" : true, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : true, - "isEnabled" : true - } - }, - { - "name" : "", - "id" : 10569219053221979239, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true + "isEnabledInHierarchy" : true }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], "mesh" : { "expectedMaterialCount" : 1, "bounds" : { - "min" : [ - -0.05348537489771843, - -0.05348537489771843, - -0.05348537489771843 - ], "max" : [ - 0.05348537489771843, - 0.05348537489771843, - 0.05348537489771843 + 0.089053646, + 0.089053646, + 0.089053646 + ], + "min" : [ + -0.089053646, + -0.089053646, + -0.089053646 ] } }, + "materials" : [ + { + + } + ], "boundsMargin" : 0 } } - }, - "componentType" : { - "model" : { - - } } }, { @@ -1198,7 +2555,7 @@ "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 872824590102831148 + "identifier" : 15887815604025352379 } } }, @@ -1209,20 +2566,14 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - -0.43961501121520996, - 0.36602920293807983, - 0.040499329566955566 - ], "matrix" : [ [ 1, @@ -1243,9 +2594,9 @@ 0 ], [ - -0.43961501121520996, - 0.36602920293807983, - 0.040499329566955566, + -0.79888666, + 0.42699748, + 0.12862957, 1 ] ], @@ -1253,86 +2604,101 @@ 1, 1, 1 + ], + "translation" : [ + -0.79888666, + 0.42699748, + 0.12862957 + ], + "rotation" : [ + 0, + 0, + 0, + 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "name" : "", + "isAccessibilityElement" : false, + "id" : 1943590921240781058, + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + } + }, + { "entityType" : { "entity" : { } }, - "availableAnimations" : [ - - ], + "id" : 12731754449330592548, + "isAccessibilityElement" : false, "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : true, - "isEnabled" : true - } - }, - { - "name" : "", - "id" : 6833819534193249208, - "isAccessibilityElement" : false, + "isEnabledInHierarchy" : true, + "isActive" : true + }, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 14858563986960262752 }, + "name" : "", + "availableAnimations" : [ + + ], "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], "mesh" : { "expectedMaterialCount" : 1, "bounds" : { - "min" : [ - -0.063612475991249084, - -0.063612475991249084, - -0.063612475991249084 - ], "max" : [ - 0.063612475991249084, - 0.063612475991249084, - 0.063612475991249084 + 0.0540813, + 0.0540813, + 0.0540813 + ], + "min" : [ + -0.0540813, + -0.0540813, + -0.0540813 ] } }, + "materials" : [ + { + + } + ], "boundsMargin" : 0 } } - }, - "componentType" : { - "model" : { - - } } }, { "properties" : { "synchronization" : { "_0" : { + "identifier" : 14561427468223218857, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 15062913396668595197 + "ownershipTransferMode" : "autoAccept" } } }, @@ -1346,17 +2712,6 @@ "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0.62717413902282715, - 0.37965160608291626, - 0.19088399410247803 - ], "matrix" : [ [ 1, @@ -1377,12 +2732,23 @@ 0 ], [ - 0.62717413902282715, - 0.37965160608291626, - 0.19088399410247803, + -0.628785, + 0.10498077, + 0.9656198, 1 ] ], + "translation" : [ + -0.628785, + 0.10498077, + 0.9656198 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -1398,58 +2764,43 @@ } } ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true } }, { - "name" : "", - "id" : 3011292031483396240, "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ + "entityType" : { + "entity" : { - ], - "hasParent" : true + } }, + "id" : 5291731127237513834, + "name" : "", "components" : { "components" : [ { "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], "mesh" : { "expectedMaterialCount" : 1, "bounds" : { - "min" : [ - -0.075313933193683624, - -0.075313933193683624, - -0.075313933193683624 - ], "max" : [ - 0.075313933193683624, - 0.075313933193683624, - 0.075313933193683624 + 0.038130004, + 0.038130004, + 0.038130004 + ], + "min" : [ + -0.038130004, + -0.038130004, + -0.038130004 ] } }, + "materials" : [ + { + + } + ], "boundsMargin" : 0 } } @@ -1461,36 +2812,41 @@ } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 3573650230345003897, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 18002407931960290702 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { + "translation" : [ + 0.6195564, + 0.37707078, + -0.7602135 + ], "rotation" : [ 0, 0, 0, 1 ], - "translation" : [ - -0.22170877456665039, - 0.97611379623413086, - -0.29893505573272705 - ], "matrix" : [ [ 1, @@ -1511,9 +2867,9 @@ 0 ], [ - -0.22170877456665039, - 0.97611379623413086, - -0.29893505573272705, + 0.6195564, + 0.37707078, + -0.7602135, 1 ] ], @@ -1524,83 +2880,92 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { + "state" : { + "isAnchored" : true, + "isEnabled" : true, + "isActive" : true, + "isEnabledInHierarchy" : true + }, + "hierarhy" : { + "children" : [ - } + ], + "parentID" : 14858563986960262752 }, "availableAnimations" : [ - ], + ] + }, + { "state" : { + "isEnabled" : true, "isActive" : true, - "isEnabledInHierarchy" : true, "isAnchored" : true, - "isEnabled" : true - } - }, - { - "name" : "", - "id" : 10108290611282443319, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + + ], "isAccessibilityElement" : false, + "entityType" : { + "entity" : { + + } + }, + "name" : "", "hierarhy" : { + "parentID" : 14858563986960262752, "children" : [ - ], - "hasParent" : true + ] }, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { + "boundsMargin" : 0, "materials" : [ { } ], "mesh" : { - "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.047364242374897003, - -0.047364242374897003, - -0.047364242374897003 + -0.09883827, + -0.09883827, + -0.09883827 ], "max" : [ - 0.047364242374897003, - 0.047364242374897003, - 0.047364242374897003 + 0.09883827, + 0.09883827, + 0.09883827 ] - } - }, - "boundsMargin" : 0 + }, + "expectedMaterialCount" : 1 + } } } - }, - "componentType" : { - "model" : { - - } } }, { "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, + "identifier" : 494223413995165166, "ownershipTransferMode" : "autoAccept", - "identifier" : 14949622812967594102 + "isOwner" : true } } }, @@ -1611,20 +2976,19 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], - "translation" : [ - -0.1472477912902832, - 0.9177558422088623, - -0.2454376220703125 - ], "matrix" : [ [ 1, @@ -1645,120 +3009,112 @@ 0 ], [ - -0.1472477912902832, - 0.9177558422088623, - -0.2454376220703125, + -0.90271485, + 0.6714249, + -0.42278206, 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 + ], + "translation" : [ + -0.90271485, + 0.6714249, + -0.42278206 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true - } + "id" : 1307850878612496537 }, { "name" : "", - "id" : 9134500930220169473, "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true + "state" : { + "isEnabled" : true, + "isAnchored" : true, + "isActive" : true, + "isEnabledInHierarchy" : true }, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], "mesh" : { "expectedMaterialCount" : 1, "bounds" : { - "min" : [ - -0.033252336084842682, - -0.033252336084842682, - -0.033252336084842682 - ], "max" : [ - 0.033252336084842682, - 0.033252336084842682, - 0.033252336084842682 + 0.038931333, + 0.038931333, + 0.038931333 + ], + "min" : [ + -0.038931333, + -0.038931333, + -0.038931333 ] } }, - "boundsMargin" : 0 - } - } - }, - "componentType" : { - "model" : { + "boundsMargin" : 0, + "materials" : [ + { + } + ] + } } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 2766256514816823871, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 199967233877166807 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { + "translation" : [ + -0.32970226, + 0.09543085, + -0.19211042 + ], "rotation" : [ 0, 0, 0, 1 ], - "translation" : [ - 0.53736960887908936, - 0.77199631929397583, - 0.38893604278564453 - ], "matrix" : [ [ 1, @@ -1779,9 +3135,9 @@ 0 ], [ - 0.53736960887908936, - 0.77199631929397583, - 0.38893604278564453, + -0.32970226, + 0.09543085, + -0.19211042, 1 ] ], @@ -1792,11 +3148,6 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] @@ -1809,56 +3160,56 @@ "availableAnimations" : [ ], - "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true - } - }, - { - "name" : "", - "id" : 13873608601323972813, - "isAccessibilityElement" : false, + "id" : 18378464368154598205, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 14858563986960262752 + } + }, + { + "entityType" : { + "entity" : { + + } }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], "mesh" : { "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.068324796855449677, - -0.068324796855449677, - -0.068324796855449677 + -0.07899005, + -0.07899005, + -0.07899005 ], "max" : [ - 0.068324796855449677, - 0.068324796855449677, - 0.068324796855449677 + 0.07899005, + 0.07899005, + 0.07899005 ] } }, - "boundsMargin" : 0 - } - } - }, - "componentType" : { - "model" : { + "boundsMargin" : 0, + "materials" : [ + { + } + ] + } } } }, @@ -1866,9 +3217,9 @@ "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 15045085615434815321 + "isOwner" : true, + "identifier" : 8719404321621863292 } } }, @@ -1882,16 +3233,10 @@ "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ - 0.37508785724639893, - 0.8716055154800415, - -0.86470425128936768 + 0.15127754, + 0.21177626, + 0.6829823 ], "matrix" : [ [ @@ -1913,12 +3258,18 @@ 0 ], [ - 0.37508785724639893, - 0.8716055154800415, - -0.86470425128936768, + 0.15127754, + 0.21177626, + 0.6829823, 1 ] ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -1935,34 +3286,53 @@ } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], + "isAccessibilityElement" : false, + "name" : "", "state" : { - "isActive" : true, + "isEnabled" : true, "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true - } + "isActive" : true, + "isAnchored" : true + }, + "hierarhy" : { + "parentID" : 14858563986960262752, + "children" : [ + + ] + }, + "id" : 13982768656395486937 }, { - "name" : "", - "id" : 11420406340265135926, "isAccessibilityElement" : false, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 14858563986960262752 + }, + "name" : "", + "availableAnimations" : [ + + ], + "entityType" : { + "entity" : { + + } + }, + "state" : { + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isAnchored" : true, + "isActive" : true }, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { @@ -1971,28 +3341,23 @@ } ], + "boundsMargin" : 0, "mesh" : { - "expectedMaterialCount" : 1, "bounds" : { - "min" : [ - -0.06071297824382782, - -0.06071297824382782, - -0.06071297824382782 - ], "max" : [ - 0.06071297824382782, - 0.06071297824382782, - 0.06071297824382782 - ] - } - }, - "boundsMargin" : 0 - } - } - }, - "componentType" : { - "model" : { - + 0.030135045, + 0.030135045, + 0.030135045 + ], + "min" : [ + -0.030135045, + -0.030135045, + -0.030135045 + ] + }, + "expectedMaterialCount" : 1 + } + } } } }, @@ -2001,8 +3366,8 @@ "synchronization" : { "_0" : { "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 4929672730690538270 + "identifier" : 9409888097344426857, + "ownershipTransferMode" : "autoAccept" } } }, @@ -2013,9 +3378,19 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { + "scale" : [ + 1, + 1, + 1 + ], "rotation" : [ 0, 0, @@ -2023,9 +3398,9 @@ 1 ], "translation" : [ - 0.95975673198699951, - 0.46991091966629028, - 0.67889237403869629 + 0.32094955, + 0.9177045, + -0.03716147 ], "matrix" : [ [ @@ -2047,115 +3422,98 @@ 0 ], [ - 0.95975673198699951, - 0.46991091966629028, - 0.67889237403869629, + 0.32094955, + 0.9177045, + -0.03716147, 1 ] - ], - "scale" : [ - 1, - 1, - 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : true, - "isEnabledInHierarchy" : true, - "isAnchored" : true, - "isEnabled" : true - } + "id" : 13242635905813859798 } - ], - "hasParent" : false + ] + }, + "entityType" : { + "anchor" : { + + } + }, + "state" : { + "isAnchored" : true, + "isEnabledInHierarchy" : true, + "isActive" : true, + "isEnabled" : true }, "components" : { "components" : [ { + "componentType" : { + "anchoring" : { + + } + }, "properties" : { "anchoring" : { "_0" : { "target" : { - "world" : [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - -5, - 1 + "world" : { + "transform" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + -5, + 1 + ] ] - ] + } } } } - }, - "componentType" : { - "anchoring" : { - - } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 4803182536531351163, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 9429855703273127634 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ 0, 0, @@ -2187,6 +3545,12 @@ 1 ] ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -2202,84 +3566,199 @@ } } ] - }, + } + }, + { + "isAccessibilityElement" : false, "entityType" : { "anchor" : { } }, - "availableAnimations" : [ - - ], - "name" : "Le Anchor" - }, - { - "name" : "", - "id" : 15411787880722809485, - "isAccessibilityElement" : false, + "state" : { + "isActive" : false, + "isEnabled" : true, + "isAnchored" : false, + "isEnabledInHierarchy" : true + }, "hierarhy" : { "children" : [ { - "name" : "\/", - "id" : 7548883642309947117, - "isAccessibilityElement" : false, + "entityType" : { + "entity" : { + + } + }, + "availableAnimations" : [ + + ], + "id" : 17004983439918564509, + "state" : { + "isActive" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isAnchored" : false + }, "hierarhy" : { "children" : [ { - "name" : "toy_car", - "id" : 10903285967301346520, - "isAccessibilityElement" : false, + "state" : { + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isAnchored" : false, + "isActive" : false + }, + "availableAnimations" : [ + + ], + "entityType" : { + "entity" : { + + } + }, "hierarhy" : { + "parentID" : 17004983439918564509, "children" : [ { - "name" : "toy_car", - "id" : 5998536565140457538, + "components" : { + "components" : [ + { + "properties" : { + "synchronization" : { + "_0" : { + "ownershipTransferMode" : "autoAccept", + "isOwner" : true, + "identifier" : 10058201519894446022 + } + } + }, + "componentType" : { + "synchronization" : { + + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1 + ] + ], + "translation" : [ + 0, + 0, + 0 + ], + "scale" : [ + 1, + 1, + 1 + ] + } + } + } + } + ] + }, + "entityType" : { + "entity" : { + + } + }, + "id" : 8635398742849809842, "isAccessibilityElement" : false, + "state" : { + "isActive" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isAnchored" : false + }, + "availableAnimations" : [ + + ], + "name" : "toy_car", "hierarhy" : { "children" : [ { "name" : "Looks", - "id" : 1700896153720873370, + "entityType" : { + "entity" : { + + } + }, + "state" : { + "isEnabledInHierarchy" : true, + "isAnchored" : false, + "isActive" : false, + "isEnabled" : true + }, + "id" : 4875998685190985122, "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ + "availableAnimations" : [ - ], - "hasParent" : true - }, + ], "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 13081578388279447520 + "isOwner" : true, + "identifier" : 1621685466125861842 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -2306,80 +3785,92 @@ 1 ] ], + "translation" : [ + 0, + 0, + 0 + ], "scale" : [ 1, 1, 1 + ], + "rotation" : [ + 0, + 0, + 0, + 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { + "hierarhy" : { + "parentID" : 8635398742849809842, + "children" : [ - } - }, + ] + } + }, + { + "name" : "toy_car_realistic_lod0", "availableAnimations" : [ ], "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "toy_car_realistic_lod0", - "id" : 11430753690554353071, - "isAccessibilityElement" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isActive" : false + }, + "entityType" : { + "entity" : { + + } + }, + "id" : 7376125305858964341, "hierarhy" : { + "parentID" : 8635398742849809842, "children" : [ - ], - "hasParent" : true + ] }, + "isAccessibilityElement" : false, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], + "boundsMargin" : 0, "mesh" : { "expectedMaterialCount" : 1, "bounds" : { - "min" : [ - -6.2201743125915527, - -1.430511474609375e-06, - -15.655849456787109 - ], "max" : [ - 6.2201747894287109, - 11.605673789978027, - 15.655849456787109 + 6.220175, + 11.605674, + 15.655849 + ], + "min" : [ + -6.2201743, + -1.4305115e-06, + -15.655849 ] } }, - "boundsMargin" : 0 - } - } - }, - "componentType" : { - "model" : { + "materials" : [ + { + } + ] + } } } }, @@ -2387,195 +3878,92 @@ "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 7869918460461983505 - } - } - }, - "componentType" : { - "synchronization" : { - - } - } - }, - { - "properties" : { - "transform" : { - "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], - "matrix" : [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - 0, - 1 - ] - ], - "scale" : [ - 1, - 1, - 1 - ] + "identifier" : 354661049148834294, + "isOwner" : true } } }, "componentType" : { - "transform" : { - - } - } - } - ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - } - ], - "hasParent" : true - }, - "components" : { - "components" : [ - { - "properties" : { - "synchronization" : { - "_0" : { - "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 9004635910268832132 - } - } - }, - "componentType" : { - "synchronization" : { - - } - } - }, - { - "properties" : { - "transform" : { - "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], - "matrix" : [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - 0, - 1 - ] - ], - "scale" : [ - 1, - 1, - 1 - ] - } - } - }, - "componentType" : { - "transform" : { + "synchronization" : { - } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "translation" : [ + 0, + 0, + 0 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1 + ] + ], + "scale" : [ + 1, + 1, + 1 + ] + } + } + } + } + ] } } - ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true + ], + "parentID" : 15728298361915322626 } } - ], - "hasParent" : true + ] }, + "isAccessibilityElement" : false, "components" : { "components" : [ { "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, + "identifier" : 16353617515793519827, "ownershipTransferMode" : "autoAccept", - "identifier" : 6728226085587809096 + "isOwner" : true } } }, @@ -2586,20 +3974,19 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -2626,22 +4013,33 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 + ], + "translation" : [ + 0, + 0, + 0 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "id" : 15728298361915322626, + "name" : "toy_car" + }, + { + "state" : { + "isActive" : false, + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isAnchored" : false + }, "entityType" : { "entity" : { @@ -2650,31 +4048,22 @@ "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "", - "id" : 9956559564985291074, "isAccessibilityElement" : false, "hierarhy" : { + "parentID" : 17004983439918564509, "children" : [ - ], - "hasParent" : true + ] }, + "id" : 17340015619639740047, "components" : { "components" : [ { "properties" : { "directionalLightShadow" : { "_0" : { - "maximumDistance" : 5, - "depthBias" : 1 + "depthBias" : 1, + "maximumDistance" : 5 } } }, @@ -2688,10 +4077,10 @@ "properties" : { "spotLight" : { "_0" : { - "outerAngleInDegrees" : 60, - "innerAngleInDegrees" : 39.999996185302734, + "innerAngleInDegrees" : 39.999996, + "attenuationRadius" : 10, "intensity" : 1000000, - "attenuationRadius" : 10 + "outerAngleInDegrees" : 60 } } }, @@ -2702,67 +4091,67 @@ } }, { - "properties" : { + "componentType" : { "spotLightShadow" : { - "_0" : { - } } }, - "componentType" : { + "properties" : { "spotLightShadow" : { + "_0" : { + } } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 10235150202954692016, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 10547934777053547843 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0.23085592687129974, - 0.73683822154998779, - 0.5573354959487915, - -0.30520844459533691 - ], "translation" : [ 0, 5, -5 ], + "rotation" : [ + 0.23085593, + 0.7368382, + 0.5573355, + -0.30520844 + ], "matrix" : [ [ - -0.7071068286895752, - -6.9327342089309241e-08, - 0.70710688829421997, + -0.70710677, + -6.932734e-08, + 0.7071069, 0 ], [ - 0.68041396141052246, - 0.27216535806655884, - 0.68041384220123291, + 0.68041396, + 0.27216545, + 0.68041384, 0 ], [ - -0.19245009124279022, - 0.96225053071975708, - -0.19245004653930664, + -0.19245009, + 0.96225053, + -0.19244997, 0 ], [ @@ -2788,79 +4177,79 @@ } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + "name" : "" }, { "name" : "", - "id" : 15760346543864022072, - "isAccessibilityElement" : false, + "id" : 8413811560989460194, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 17004983439918564509 + }, + "isAccessibilityElement" : false, + "entityType" : { + "entity" : { + + } }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { - "properties" : { + "componentType" : { "pointLight" : { - "_0" : { - "attenuationRadius" : 10, - "intensity" : 26963.759765625 - } + } }, - "componentType" : { + "properties" : { "pointLight" : { - + "_0" : { + "intensity" : 26963.76, + "attenuationRadius" : 10 + } } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 11230605617970149458, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 3323837393641110455 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { + "scale" : [ + 1, + 1, + 1 + ], "rotation" : [ 0, 0, 0, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -2887,49 +4276,38 @@ 1 ] ], - "scale" : [ - 1, - 1, - 1 + "translation" : [ + 0, + 0, + 0 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], "state" : { - "isActive" : false, "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true + "isEnabled" : true, + "isActive" : false, + "isAnchored" : false } } ], - "hasParent" : true + "parentID" : 8869612504161882199 }, + "isAccessibilityElement" : false, + "name" : "\/", "components" : { "components" : [ { "properties" : { "synchronization" : { "_0" : { + "identifier" : 16427773019462848083, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 6315283977768004437 + "ownershipTransferMode" : "autoAccept" } } }, @@ -2940,37 +4318,41 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0.80378448963165283, - 0, - 0.59492063522338867 - ], "translation" : [ -0.5, 0, -0.25 ], + "scale" : [ + 1, + 1, + 1 + ], "matrix" : [ [ - -0.29213905334472656, + -0.29213896, 0, - -0.95637595653533936, + -0.95637596, 0 ], [ 0, - 1, + 1.0000001, 0, 0 ], [ - 0.95637595653533936, + 0.95637596, 0, - -0.29213905334472656, + -0.29213893, 0 ], [ @@ -2980,41 +4362,25 @@ 1 ] ], - "scale" : [ - 1, - 1, - 1 + "rotation" : [ + 0, + 0.8037845, + 0, + 0.59492064 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] - }, - "entityType" : { - "entity" : { - - } - - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } - ], - "hasParent" : false + ] }, + "availableAnimations" : [ + + ], + "name" : "", "components" : { "components" : [ { @@ -3022,10 +4388,18 @@ "anchoring" : { "_0" : { "target" : { - "plane" : [ - 0, - 0 - ] + "plane" : { + "classifications" : [ + 18446744073709551615 + ], + "_0" : [ + 255 + ], + "minimumBounds" : [ + 0, + 0 + ] + } } } } @@ -3037,36 +4411,35 @@ } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 6139751611841533673, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 14259925259721833214 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ 0, 0, 0 ], + "scale" : [ + 1, + 1, + 1 + ], "matrix" : [ [ 1, @@ -3093,9 +4466,10 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 ] } @@ -3109,6 +4483,17 @@ } ] }, + "id" : 8869612504161882199 + }, + { + "id" : 17755668076156285797, + "state" : { + "isAnchored" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isActive" : false + }, + "isAccessibilityElement" : false, "entityType" : { "anchor" : { @@ -3117,99 +4502,113 @@ "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { "name" : "RobotAnchor", - "id" : 17397063142267904538, - "isAccessibilityElement" : false, "hierarhy" : { "children" : [ { + "accessibilityDescription" : "", "name" : "\/", + "accessibilityLabel" : "Le robot accessibilityLabel", "state" : { "isActive" : false, - "isEnabledInHierarchy" : true, "isAnchored" : false, - "isEnabled" : true + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + { + "name" : "global scene animation" + } + ], + "entityType" : { + "entity" : { + + } }, "isAccessibilityElement" : true, - "id" : 13524967467058063214, - "accessibilityLabel" : "Le robot accessibilityLabel", "hierarhy" : { "children" : [ { "name" : "robot_walk_idle", - "id" : 14756045127838025832, - "isAccessibilityElement" : false, + "id" : 12633193968055825291, "hierarhy" : { + "parentID" : 18326093267967839324, "children" : [ { "name" : "robot_rig", - "id" : 16394107879663119920, - "isAccessibilityElement" : false, "hierarhy" : { + "parentID" : 12633193968055825291, "children" : [ { + "availableAnimations" : [ + + ], + "state" : { + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isActive" : false, + "isAnchored" : false + }, "name" : "robot_skinned_mesh", - "id" : 6904275369718444634, "isAccessibilityElement" : false, + "id" : 11276765646201870382, "hierarhy" : { "children" : [ { - "name" : "robot_mesh_robot_geo", - "id" : 15960019781644464281, + "id" : 10816499451904027660, "isAccessibilityElement" : false, + "entityType" : { + "entity" : { + + } + }, + "state" : { + "isActive" : false, + "isAnchored" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, "hierarhy" : { "children" : [ { - "name" : "robot_mesh_mesh_export_neck_PLY", - "id" : 6151143559082905622, - "isAccessibilityElement" : false, + "id" : 5142495948905459876, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 10816499451904027660 }, + "availableAnimations" : [ + + ], + "name" : "robot_mesh_mesh_export_neck_PLY", "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 7680568631541895491 + "isOwner" : true, + "identifier" : 16247173066880652914 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -3236,6 +4635,17 @@ 1 ] ], + "translation" : [ + 0, + 0, + 0 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -3243,39 +4653,46 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "isAccessibilityElement" : false, "entityType" : { "entity" : { } }, - "availableAnimations" : [ - - ], "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : false, - "isEnabled" : true + "isEnabledInHierarchy" : true, + "isActive" : false } }, { - "name" : "robot_mesh_mesh_export_right_ankle_PLY", - "id" : 3434751271037885984, "isAccessibilityElement" : false, + "id" : 2045471745276158649, + "availableAnimations" : [ + + ], + "name" : "robot_mesh_mesh_export_right_ankle_PLY", "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 10816499451904027660 + }, + "state" : { + "isEnabled" : true, + "isAnchored" : false, + "isEnabledInHierarchy" : true, + "isActive" : false + }, + "entityType" : { + "entity" : { + + } }, "components" : { "components" : [ @@ -3283,9 +4700,9 @@ "properties" : { "synchronization" : { "_0" : { + "identifier" : 15313450742506449527, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 15208737002483131834 + "ownershipTransferMode" : "autoAccept" } } }, @@ -3299,17 +4716,6 @@ "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -3336,6 +4742,17 @@ 1 ] ], + "translation" : [ + 0, + 0, + 0 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -3351,41 +4768,32 @@ } } ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } }, { - "name" : "robot_mesh_mesh_export_left_ankle_PLY", - "id" : 16245150876351209138, - "isAccessibilityElement" : false, + "state" : { + "isAnchored" : false, + "isEnabledInHierarchy" : true, + "isActive" : false, + "isEnabled" : true + }, "hierarhy" : { + "parentID" : 10816499451904027660, "children" : [ - ], - "hasParent" : true + ] }, + "name" : "robot_mesh_mesh_export_left_ankle_PLY", + "id" : 6780665925198118232, "components" : { "components" : [ { "properties" : { "synchronization" : { "_0" : { + "identifier" : 17712542170457532988, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 685825532185007509 + "ownershipTransferMode" : "autoAccept" } } }, @@ -3396,6 +4804,11 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { @@ -3405,10 +4818,10 @@ 0, 1 ], - "translation" : [ - 0, - 0, - 0 + "scale" : [ + 1, + 1, + 1 ], "matrix" : [ [ @@ -3436,79 +4849,68 @@ 1 ] ], - "scale" : [ - 1, - 1, - 1 + "translation" : [ + 0, + 0, + 0 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "availableAnimations" : [ + + ], "entityType" : { "entity" : { } }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + "isAccessibilityElement" : false }, { - "name" : "robot_mesh_mesh_export_head_PLY", - "id" : 12359940052049230976, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ + "availableAnimations" : [ - ], - "hasParent" : true - }, + ], "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, + "identifier" : 17496068288950263443, "ownershipTransferMode" : "autoAccept", - "identifier" : 14461844653389229389 + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, + "translation" : [ 0, 0, - 1 + 0 ], - "translation" : [ + "rotation" : [ 0, 0, - 0 + 0, + 1 ], "matrix" : [ [ @@ -3543,11 +4945,6 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] @@ -3557,59 +4954,51 @@ } }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "robot_mesh_mesh_export_left_arm_PLY", - "id" : 10836546235265570368, "isAccessibilityElement" : false, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 10816499451904027660 }, + "id" : 5462102729902086236, + "name" : "robot_mesh_mesh_export_head_PLY", + "state" : { + "isAnchored" : false, + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isActive" : false + } + }, + { + "id" : 16848100296713323142, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 9928375688159277106 + "identifier" : 7708413599905690328 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -3640,62 +5029,94 @@ 1, 1, 1 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "translation" : [ + 0, + 0, + 0 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, "availableAnimations" : [ ], "state" : { "isActive" : false, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : false, - "isEnabled" : true - } + "isEnabledInHierarchy" : true + }, + "entityType" : { + "entity" : { + + } + }, + "isAccessibilityElement" : false, + "hierarhy" : { + "parentID" : 10816499451904027660, + "children" : [ + + ] + }, + "name" : "robot_mesh_mesh_export_left_arm_PLY" }, { "name" : "robot_mesh_mesh_export_left_leg_PLY", - "id" : 5520430718046965515, + "state" : { + "isActive" : false, + "isEnabled" : true, + "isAnchored" : false, + "isEnabledInHierarchy" : true + }, + "entityType" : { + "entity" : { + + } + }, + "availableAnimations" : [ + + ], "isAccessibilityElement" : false, "hierarhy" : { + "parentID" : 10816499451904027660, "children" : [ - ], - "hasParent" : true + ] }, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 2712591250510788762 + "identifier" : 4050966784478294320, + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { @@ -3743,66 +5164,48 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "id" : 11582275059860442627 + }, + { "entityType" : { "entity" : { } }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "robot_mesh_mesh_export_right_leg_PLY", - "id" : 5714694127074419098, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true - }, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 9169797846235451867 + "identifier" : 15862444524209716516, + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], "translation" : [ @@ -3836,56 +5239,47 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "robot_mesh_mesh_export_body_PLY", - "id" : 16237442439255284025, - "isAccessibilityElement" : false, + "name" : "robot_mesh_mesh_export_right_leg_PLY", + "id" : 17447805604113554636, "hierarhy" : { + "parentID" : 10816499451904027660, "children" : [ - ], - "hasParent" : true + ] + }, + "availableAnimations" : [ + + ], + "state" : { + "isActive" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isAnchored" : false }, + "isAccessibilityElement" : false + }, + { "components" : { "components" : [ { "properties" : { "synchronization" : { "_0" : { + "identifier" : 10782510853036440555, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 2043885319379791061 + "ownershipTransferMode" : "autoAccept" } } }, @@ -3896,20 +5290,14 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -3936,6 +5324,17 @@ 1 ] ], + "translation" : [ + 0, + 0, + 0 + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -3943,39 +5342,46 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { + "name" : "robot_mesh_mesh_export_body_PLY", + "isAccessibilityElement" : false, + "id" : 4234577135485721304, + "state" : { + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isAnchored" : false, + "isActive" : false + }, + "hierarhy" : { + "parentID" : 10816499451904027660, + "children" : [ - } + ] }, "availableAnimations" : [ ], + "entityType" : { + "entity" : { + + } + } + }, + { + "id" : 493369888419780716, "state" : { - "isActive" : false, "isEnabledInHierarchy" : true, + "isActive" : false, "isAnchored" : false, "isEnabled" : true - } - }, - { - "name" : "robot_mesh_mesh_export_right_arm_PLY", - "id" : 12794118269029478361, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ + }, + "entityType" : { + "entity" : { - ], - "hasParent" : true + } }, "components" : { "components" : [ @@ -3983,9 +5389,9 @@ "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 17124006275562721912 + "identifier" : 17688872543288416721, + "isOwner" : true } } }, @@ -3996,15 +5402,14 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ 0, 0, @@ -4036,6 +5441,12 @@ 1 ] ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, @@ -4043,73 +5454,48 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } - }, - { - "name" : "robot_mesh_mesh_export_right_foot_PLY", - "id" : 3019325771996444184, - "isAccessibilityElement" : false, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 10816499451904027660 }, + "isAccessibilityElement" : false, + "name" : "robot_mesh_mesh_export_right_arm_PLY" + }, + { + "name" : "robot_mesh_mesh_export_right_foot_PLY", + "id" : 3026541971679476935, + "isAccessibilityElement" : false, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 4913574249690243511, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 656588899058322967 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -4136,10 +5522,21 @@ 1 ] ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], "scale" : [ 1, 1, 1 + ], + "translation" : [ + 0, + 0, + 0 ] } } @@ -4152,57 +5549,54 @@ } ] }, + "availableAnimations" : [ + + ], "entityType" : { "entity" : { } }, - "availableAnimations" : [ + "hierarhy" : { + "children" : [ - ], + ], + "parentID" : 10816499451904027660 + }, "state" : { "isActive" : false, "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true + "isEnabled" : true, + "isAnchored" : false } }, { "name" : "robot_mesh_mesh_export_left_foot_PLY", - "id" : 13589819844428907004, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true - }, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 16814930193678159892 + "identifier" : 5554951018847787645 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], "translation" : [ @@ -4236,9 +5630,10 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 ] } @@ -4252,31 +5647,30 @@ } ] }, - "entityType" : { - "entity" : { + "hierarhy" : { + "parentID" : 10816499451904027660, + "children" : [ - } + ] + }, + "isAccessibilityElement" : false, + "state" : { + "isEnabled" : true, + "isAnchored" : false, + "isActive" : false, + "isEnabledInHierarchy" : true }, + "id" : 2772370477699597252, "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true + "entityType" : { + "entity" : { + + } } }, { - "name" : "robot_mesh_mesh_export_spin_PLY", - "id" : 9569908341405492399, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true - }, "components" : { "components" : [ { @@ -4285,7 +5679,7 @@ "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 5424024734179293470 + "identifier" : 9334284169103192424 } } }, @@ -4296,6 +5690,11 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { @@ -4305,6 +5704,11 @@ 0, 1 ], + "scale" : [ + 1, + 1, + 1 + ], "translation" : [ 0, 0, @@ -4335,41 +5739,44 @@ 0, 1 ] - ], - "scale" : [ - 1, - 1, - 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, + "name" : "robot_mesh_mesh_export_spin_PLY", + "isAccessibilityElement" : false, "availableAnimations" : [ ], "state" : { "isActive" : false, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : false, - "isEnabled" : true + "isEnabledInHierarchy" : true + }, + "hierarhy" : { + "parentID" : 10816499451904027660, + "children" : [ + + ] + }, + "id" : 2701369247000588545, + "entityType" : { + "entity" : { + + } } } ], - "hasParent" : true + "parentID" : 11276765646201870382 }, + "name" : "robot_mesh_robot_geo", + "availableAnimations" : [ + + ], "components" : { "components" : [ { @@ -4378,7 +5785,7 @@ "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 1924047811731154890 + "identifier" : 9809643270193507500 } } }, @@ -4398,11 +5805,6 @@ 0, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -4433,6 +5835,11 @@ 1, 1, 1 + ], + "translation" : [ + 0, + 0, + 0 ] } } @@ -4444,71 +5851,124 @@ } } ] - }, - "entityType" : { - "entity" : { - - } - }, + } + }, + { "availableAnimations" : [ - + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "Animation" + }, + { + "name" : "default scene animation" + }, + { + "name" : "default subtree animation" + } ], "state" : { "isActive" : false, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : false, - "isEnabled" : true - } - }, - { + "isEnabledInHierarchy" : true + }, "name" : "robot_bind", - "id" : 14947556965855028983, - "isAccessibilityElement" : false, "hierarhy" : { + "parentID" : 11276765646201870382, "children" : [ { + "availableAnimations" : [ + + ], + "state" : { + "isEnabledInHierarchy" : true, + "isAnchored" : false, + "isEnabled" : true, + "isActive" : false + }, "name" : "root", - "id" : 16501443592857541884, + "entityType" : { + "entity" : { + + } + }, "isAccessibilityElement" : false, "hierarhy" : { + "parentID" : 15770533885507784683, "children" : [ - ], - "hasParent" : true + ] }, + "id" : 2578965996846646071, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 18385887861732561659 + "identifier" : 8364652438624566378 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ 0, 0, 0 ], + "scale" : [ + 1, + 1, + 1 + ], "matrix" : [ [ 1, @@ -4535,66 +5995,54 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } - ], - "hasParent" : true + ] + }, + "entityType" : { + "entity" : { + + } }, + "isAccessibilityElement" : false, + "id" : 15770533885507784683, "components" : { "components" : [ { "properties" : { "model" : { "_0" : { - "materials" : [ - { - - } - ], "mesh" : { - "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -7.1308999061584473, - 0.0043735499493777752, - -9.3909997940063477 + -7.1309, + 0.00437355, + -9.391 ], "max" : [ - 7.1308999061584473, - 27.392299652099609, - 7.4211797714233398 + 7.1309, + 27.3923, + 7.42118 ] - } + }, + "expectedMaterialCount" : 1 }, + "materials" : [ + { + + } + ], "boundsMargin" : 0 } } @@ -4609,9 +6057,9 @@ "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, + "identifier" : 11883632196825281390, "ownershipTransferMode" : "autoAccept", - "identifier" : 6158176545416761545 + "isOwner" : true } } }, @@ -4625,147 +6073,85 @@ "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], - "matrix" : [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - 0, - 1 - ] - ], "scale" : [ 1, 1, 1 - ] - } - } - }, - "componentType" : { - "transform" : { - - } - } - } - ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "Animation" - }, - { - "name" : "default scene animation" - }, - { - "name" : "default subtree animation" - } - ], + ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "translation" : [ + 0, + 0, + 0 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1 + ] + ] + } + } + }, + "componentType" : { + "transform" : { - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true + } + } + } + ] } } ], - "hasParent" : true + "parentID" : 12990209049311204547 }, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 14217231894495224127 + "identifier" : 17555556699751840717 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ 0, 0, @@ -4801,6 +6187,12 @@ 1, 1, 1 + ], + "rotation" : [ + 0, + 0, + 0, + 1 ] } } @@ -4817,39 +6209,50 @@ "entity" : { } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } - ], - "hasParent" : true + ] + }, + "state" : { + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isActive" : false, + "isAnchored" : false + }, + "entityType" : { + "entity" : { + + } }, + "id" : 12990209049311204547, + "availableAnimations" : [ + + ], + "isAccessibilityElement" : false, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 7188200759245067465 + "isOwner" : true, + "identifier" : 7861685996543716201 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { @@ -4859,11 +6262,6 @@ 0, 1 ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -4894,52 +6292,40 @@ 1, 1, 1 + ], + "translation" : [ + 0, + 0, + 0 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] - }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } - ], - "hasParent" : true + ] }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 15716932701968482745 + "identifier" : 15525681203575316313, + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { @@ -4999,46 +6385,54 @@ } ] }, + "isAccessibilityElement" : false, "entityType" : { "entity" : { } }, - "availableAnimations" : [ - - ], "state" : { "isActive" : false, - "isEnabledInHierarchy" : true, + "isEnabled" : true, "isAnchored" : false, - "isEnabled" : true + "isEnabledInHierarchy" : true } } ], - "hasParent" : true + "parentID" : 17755668076156285797 }, "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { + "identifier" : 2604610167304378661, "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 2180034876844334762 + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { + "scale" : [ + 0.01, + 0.01, + 0.01 + ], "rotation" : [ 0, 0, @@ -5052,21 +6446,21 @@ ], "matrix" : [ [ - 0.0099999997764825821, + 0.01, 0, 0, 0 ], [ 0, - 0.0099999997764825821, + 0.01, 0, 0 ], [ 0, 0, - 0.0099999997764825821, + 0.01, 0 ], [ @@ -5075,37 +6469,16 @@ 0, 1 ] - ], - "scale" : [ - 0.0099999997764825821, - 0.0099999997764825821, - 0.0099999997764825821 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, - "entityType" : { - "entity" : { - - } - }, - "accessibilityDescription" : "", - "availableAnimations" : [ - { - "name" : "global scene animation" - } - ] + "id" : 18326093267967839324 } - ], - "hasParent" : false + ] }, "components" : { "components" : [ @@ -5114,10 +6487,18 @@ "anchoring" : { "_0" : { "target" : { - "plane" : [ - 0, - 0 - ] + "plane" : { + "classifications" : [ + 18446744073709551615 + ], + "_0" : [ + 255 + ], + "minimumBounds" : [ + 0, + 0 + ] + } } } } @@ -5132,9 +6513,9 @@ "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 1421716892748975436 + "identifier" : 16449333663491879719, + "isOwner" : true } } }, @@ -5145,20 +6526,19 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], - "translation" : [ - 0, - 0, - 0.5 - ], "matrix" : [ [ 1, @@ -5185,39 +6565,26 @@ 1 ] ], - "scale" : [ - 1, - 1, + "translation" : [ + 0, + 0, + 0.5 + ], + "rotation" : [ + 0, + 0, + 0, 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] - }, - "entityType" : { - "anchor" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } ] }, - "debugOptionsRawValue" : 17, - "contentScaleFactor" : 3 + "contentScaleFactor" : 3, + "debugOptionsRawValue" : 0 } diff --git a/Sources/MultipeerClient/Resources/Mock/simple_arview.json b/Sources/MultipeerClient/Resources/Mock/simple_arview.json index 37d9968..09660f0 100644 --- a/Sources/MultipeerClient/Resources/Mock/simple_arview.json +++ b/Sources/MultipeerClient/Resources/Mock/simple_arview.json @@ -1,43 +1,148 @@ { + "contentScaleFactor" : 3, "scene" : { "anchors" : [ { - "name" : "", - "id" : 10798020353963348595, - "isAccessibilityElement" : false, + "id" : 2483588098358084184, "hierarhy" : { "children" : [ { "name" : "", - "id" : 18359438271439800841, - "isAccessibilityElement" : false, + "state" : { + "isActive" : false, + "isEnabledInHierarchy" : true, + "isEnabled" : true, + "isAnchored" : false + }, + "id" : 15531501328511675032, "hierarhy" : { + "parentID" : 2483588098358084184, "children" : [ { - "name" : "", - "id" : 15038790943578240344, - "isAccessibilityElement" : false, "hierarhy" : { + "parentID" : 15531501328511675032, "children" : [ { + "components" : { + "components" : [ + { + "componentType" : { + "synchronization" : { + + } + }, + "properties" : { + "synchronization" : { + "_0" : { + "isOwner" : true, + "ownershipTransferMode" : "autoAccept", + "identifier" : 383895323399954579 + } + } + } + }, + { + "componentType" : { + "transform" : { + + } + }, + "properties" : { + "transform" : { + "_0" : { + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "matrix" : [ + [ + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0 + ], + [ + 0, + 0.05, + 0, + 1 + ] + ], + "translation" : [ + 0, + 0.05, + 0 + ], + "scale" : [ + 1, + 1, + 1 + ] + } + } + } + } + ] + }, + "id" : 8663982150608008035, + "state" : { + "isEnabled" : true, + "isActive" : false, + "isEnabledInHierarchy" : true, + "isAnchored" : false + }, + "entityType" : { + "entity" : { + + } + }, "name" : "Steel Box", - "id" : 672640840761579920, - "isAccessibilityElement" : false, "hierarhy" : { "children" : [ { - "name" : "simpBld_root", - "id" : 9648984824774598611, - "isAccessibilityElement" : false, + "availableAnimations" : [ + + ], + "entityType" : { + "entity" : { + + } + }, + "id" : 16637816425975897603, + "state" : { + "isEnabled" : true, + "isAnchored" : false, + "isActive" : false, + "isEnabledInHierarchy" : true + }, "hierarhy" : { "children" : [ ], - "hasParent" : true + "parentID" : 8663982150608008035 }, "components" : { "components" : [ { + "componentType" : { + "model" : { + + } + }, "properties" : { "model" : { "_0" : { @@ -47,28 +152,23 @@ } ], "mesh" : { - "expectedMaterialCount" : 1, "bounds" : { "min" : [ - -0.05000000074505806, - -0.05000000074505806, - -0.05000000074505806 + -0.05, + -0.05, + -0.05 ], "max" : [ - 0.05000000074505806, - 0.05000000074505806, - 0.05000000074505806 + 0.05, + 0.05, + 0.05 ] - } + }, + "expectedMaterialCount" : 1 }, "boundsMargin" : 0 } } - }, - "componentType" : { - "model" : { - - } } }, { @@ -76,8 +176,8 @@ "synchronization" : { "_0" : { "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 15912208395330172194 + "identifier" : 5363323842274640034, + "ownershipTransferMode" : "autoAccept" } } }, @@ -102,6 +202,11 @@ 0, 0 ], + "scale" : [ + 1, + 1, + 1 + ], "matrix" : [ [ 1, @@ -127,11 +232,6 @@ 0, 1 ] - ], - "scale" : [ - 1, - 1, - 1 ] } } @@ -144,150 +244,54 @@ } ] }, - "entityType" : { - "entity" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + "name" : "simpBld_root", + "isAccessibilityElement" : false } ], - "hasParent" : true - }, - "components" : { - "components" : [ - { - "properties" : { - "synchronization" : { - "_0" : { - "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 4553652004896119075 - } - } - }, - "componentType" : { - "synchronization" : { - - } - } - }, - { - "properties" : { - "transform" : { - "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0.05000000074505806, - 0 - ], - "matrix" : [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0 - ], - [ - 0, - 0.05000000074505806, - 0, - 1 - ] - ], - "scale" : [ - 1, - 1, - 1 - ] - } - } - }, - "componentType" : { - "transform" : { - - } - } - } - ] - }, - "entityType" : { - "entity" : { - - } + "parentID" : 1561550102024476731 }, "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + "isAccessibilityElement" : false } - ], - "hasParent" : true + ] }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 7443260464541884213 + "identifier" : 16647246008028842643, + "isOwner" : true } } - }, - "componentType" : { - "synchronization" : { - - } } }, { "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], "translation" : [ 0, 0, 0 ], + "scale" : [ + 1, + 1, + 1 + ], "matrix" : [ [ 1, @@ -300,7 +304,6 @@ 1, 0, 0 - ], [ 0, @@ -315,9 +318,10 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 ] } @@ -331,31 +335,22 @@ } ] }, + "id" : 1561550102024476731, + "name" : "", + "state" : { + "isEnabled" : true, + "isEnabledInHierarchy" : true, + "isAnchored" : false, + "isActive" : false + }, + "isAccessibilityElement" : false, "entityType" : { "entity" : { } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } }, { - "name" : "Ground Plane", - "id" : 2523032544665650958, - "isAccessibilityElement" : false, - "hierarhy" : { - "children" : [ - - ], - "hasParent" : true - }, "components" : { "components" : [ { @@ -367,11 +362,11 @@ } ], - "mode" : "default", "filter" : { - "group" : 1, - "mask" : 1 - } + "mask" : 1, + "group" : 1 + }, + "mode" : "default" } } }, @@ -382,47 +377,52 @@ } }, { + "componentType" : { + "physicsBody" : { + + } + }, "properties" : { "physicsBody" : { "_0" : { + "mode" : "static", "isTranslationLocked" : { "x" : false, "y" : false, "z" : false }, - "isContinuousCollisionDetectionEnabled" : false, "isRotationLocked" : { - "x" : false, "y" : false, + "x" : false, "z" : false }, - "mode" : "static" + "isContinuousCollisionDetectionEnabled" : false } } - }, - "componentType" : { - "physicsBody" : { - - } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 780681697957606691 + "identifier" : 14153558297139435912 } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { @@ -437,6 +437,11 @@ 0, 0 ], + "scale" : [ + 1, + 1, + 1 + ], "matrix" : [ [ 1, @@ -462,93 +467,102 @@ 0, 1 ] - ], - "scale" : [ - 1, - 1, - 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "state" : { + "isAnchored" : false, + "isActive" : false, + "isEnabled" : true, + "isEnabledInHierarchy" : true + }, + "availableAnimations" : [ + + ], + "name" : "Ground Plane", "entityType" : { "entity" : { } }, - "availableAnimations" : [ + "hierarhy" : { + "children" : [ - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + ], + "parentID" : 15531501328511675032 + }, + "id" : 11734244072457182353, + "isAccessibilityElement" : false } - ], - "hasParent" : true + ] }, + "isAccessibilityElement" : false, + "entityType" : { + "anchor" : { + + } + }, + "availableAnimations" : [ + + ], "components" : { "components" : [ { + "componentType" : { + "anchoring" : { + + } + }, "properties" : { "anchoring" : { "_0" : { "target" : { - "plane" : [ - 0, - 0 - ] + "plane" : { + "minimumBounds" : [ + 0, + 0 + ], + "_0" : [ + 1 + ], + "classifications" : [ + 18446744073709551615 + ] + } } } } - }, - "componentType" : { - "anchoring" : { - - } } }, { + "componentType" : { + "synchronization" : { + + } + }, "properties" : { "synchronization" : { "_0" : { "isOwner" : true, - "ownershipTransferMode" : "autoAccept", - "identifier" : 10807156165815798859 + "identifier" : 14699791824862646126, + "ownershipTransferMode" : "autoAccept" } } - }, - "componentType" : { - "synchronization" : { - - } } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, - 1 - ], - "translation" : [ - 0, - 0, - 0 - ], "matrix" : [ [ 1, @@ -575,6 +589,17 @@ 1 ] ], + "rotation" : [ + 0, + 0, + 0, + 1 + ], + "translation" : [ + 0, + 0, + 0 + ], "scale" : [ 1, 1, @@ -582,32 +607,12 @@ ] } } - }, - "componentType" : { - "transform" : { - - } } } ] - }, - "entityType" : { - "anchor" : { - - } - }, - "availableAnimations" : [ - - ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true } } - ], - "hasParent" : false + ] }, "components" : { "components" : [ @@ -616,10 +621,18 @@ "anchoring" : { "_0" : { "target" : { - "plane" : [ - 0, - 0 - ] + "plane" : { + "minimumBounds" : [ + 0, + 0 + ], + "_0" : [ + 1 + ], + "classifications" : [ + 18446744073709551615 + ] + } } } } @@ -634,9 +647,9 @@ "properties" : { "synchronization" : { "_0" : { - "isOwner" : true, "ownershipTransferMode" : "autoAccept", - "identifier" : 16950790846409808805 + "identifier" : 9990575719057888320, + "isOwner" : true } } }, @@ -647,13 +660,17 @@ } }, { + "componentType" : { + "transform" : { + + } + }, "properties" : { "transform" : { "_0" : { - "rotation" : [ - 0, - 0, - 0, + "scale" : [ + 1, + 1, 1 ], "translation" : [ @@ -687,22 +704,24 @@ 1 ] ], - "scale" : [ - 1, - 1, + "rotation" : [ + 0, + 0, + 0, 1 ] } } - }, - "componentType" : { - "transform" : { - - } } } ] }, + "state" : { + "isEnabledInHierarchy" : true, + "isAnchored" : false, + "isActive" : false, + "isEnabled" : true + }, "entityType" : { "entity" : { @@ -711,15 +730,10 @@ "availableAnimations" : [ ], - "state" : { - "isActive" : false, - "isEnabledInHierarchy" : true, - "isAnchored" : false, - "isEnabled" : true - } + "isAccessibilityElement" : false, + "name" : "" } ] }, - "debugOptionsRawValue" : 0, - "contentScaleFactor" : 3 + "debugOptionsRawValue" : 0 } diff --git a/Sources/RealityCheckConnect/RealityCheckConnect.swift b/Sources/RealityCheckConnect/RealityCheckConnect.swift new file mode 100644 index 0000000..70c4df1 --- /dev/null +++ b/Sources/RealityCheckConnect/RealityCheckConnect.swift @@ -0,0 +1,7 @@ +import Foundation + +#if os(visionOS) + @_exported import RealityCheckConnect_visionOS +#elseif os(iOS) + @_exported import RealityCheckConnect_iOS +#endif diff --git a/Sources/RealityCheckConnect/RealityCheckConnectView.swift b/Sources/RealityCheckConnect/RealityCheckConnectView.swift deleted file mode 100644 index c783cb7..0000000 --- a/Sources/RealityCheckConnect/RealityCheckConnectView.swift +++ /dev/null @@ -1,425 +0,0 @@ -import Dependencies -import Models -import MultipeerClient -import RealityDumpClient -import RealityKit -import StreamingClient -import SwiftUI - -final class ViewModel: ObservableObject { - @Published var connectionState: MultipeerClient.SessionState - @Published var hostName: String - @Published var isStreaming = false - - @Dependency(\.multipeerClient) var multipeerClient - @Dependency(\.realityDump) var realityDump - @Dependency(\.streamingClient) var streamingClient - - fileprivate var arView: ARView? - - init( - connectionState: MultipeerClient.SessionState = .notConnected, - hostName: String = "...", - arView: ARView? = nil - ) { - self.connectionState = connectionState - self.hostName = hostName - self.arView = arView - } - - func startMultipeerSession() async { - //MARK: 1. Setup - for await action in await multipeerClient.start( - serviceName: "reality-check", - sessionType: .peer, - discoveryInfo: AppInfo.discoveryInfo - ) { - switch action { - case .session(let sessionAction): - switch sessionAction { - case .stateDidChange(let state): - await MainActor.run { - connectionState = state - } - if case .connected = state { - //MARK: 2. Send Hierarchy - await sendHierarchy() - } - - case .didReceiveData(let data): - //ARView Debug Options - if let debugOptions = try? JSONDecoder() - .decode( - _DebugOptions.self, - from: data - ) - { - await MainActor.run { - arView?.debugOptions = ARView.DebugOptions( - rawValue: debugOptions.rawValue - ) - } - } - } - - case .browser(_): - return - - case .advertiser(let advertiserAction): - switch advertiserAction { - case .didReceiveInvitationFromPeer(let peer): - multipeerClient.acceptInvitation() - multipeerClient.stopAdvertisingPeer() - await MainActor.run { - hostName = peer.displayName - } - } - } - } - } - - func sendHierarchy() async { - guard let arView else { - //FIXME: make a runtime error instead - fatalError("ARView is required in order to be able to send its hierarchy") - } - - let encoder = JSONEncoder() - encoder.nonConformingFloatEncodingStrategy = .convertToString( - positiveInfinity: "INF", - negativeInfinity: "-INF", - nan: "NAN" - ) - encoder.outputFormatting = .prettyPrinted - - let anchors = await arView.scene.anchors.compactMap { $0 } - var identifiableAnchors: [IdentifiableEntity] = [] - for anchor in anchors { - identifiableAnchors.append( - await realityDump.identify(anchor) - ) - } - - #if os(iOS) - let arViewData = try! await encoder.encode( - CodableARView( - arView, - anchors: identifiableAnchors, - contentScaleFactor: arView.contentScaleFactor - ) - ) - multipeerClient.send(arViewData) - print(String(data: arViewData, encoding: .utf8)!) - #else - fatalError("`arView.contentScaleFactor` cant be found on macOS") - #endif - } - - func startStreaming() async { - await MainActor.run { - isStreaming = true - } - - for await frameData in await streamingClient.startScreenCapture() { - multipeerClient.send(frameData) - } - } - - func stopStreaming() async { - await MainActor.run { - isStreaming = false - } - - streamingClient.stopScreenCapture() - } -} - -/** -Represents a SwiftUI view for controlling the RealityCheck connection and exchange of the running AR experience data. - -Use the `RealityCheckConnectView` struct to display the state of the connection and provide a user interface for establishing a connection with **RealityCheck** macOS app. Once integrated, the GUI will allow to connect and exchange AR scene hierarchy data. It utilizes SwiftUI for rendering the user interface. and can be accessed as a `LibraryItem` from the `Library` panel. - -**Usage** - -1. Initialize `RealityCheckConnectView` and provide an optional `ARView` instance to enable hierarchy sending functionality. -2. Add `RealityCheckConnectView` to your SwiftUI view hierarchy. - -**Example** - -Here's an example of how to use `RealityCheckConnectView`: -```swift -var body: some View { - ZStack { - // Other views - RealityCheckConnectView(arView) - } -} -``` - */ -public struct RealityCheckConnectView: View { - @ObservedObject private var viewModel: ViewModel - - public init() { - self.viewModel = .init() - } - - public init( - _ arView: ARView - ) { - self.viewModel = .init(arView: arView) - } - - fileprivate init( - viewModel: ViewModel - ) { - self.viewModel = viewModel - } - - var connectionStateFill: Color { - switch viewModel.connectionState { - case .notConnected: - return .red - case .connecting: - return .orange - case .connected: - return .green - } - } - - var connectionStateMessage: String { - switch viewModel.connectionState { - case .notConnected: - return "not connected" - case .connecting: - return "connecting" - case .connected: - return viewModel.isStreaming - ? " " - : "connected to: \(viewModel.hostName)" - } - } - - var isConnected: Bool { - switch viewModel.connectionState { - case .notConnected, .connecting: - return false - case .connected: - return true - } - } - - @State var location: CGPoint = CGPoint(x: 88, y: 33) - @GestureState var startLocation: CGPoint? = nil - - public var body: some View { - RoundedRectangle(cornerRadius: 32, style: .continuous) - .stroke(lineWidth: viewModel.isStreaming ? 0.5 : 3) - .fill(Material.ultraThin) - .animation(.default, value: viewModel.isStreaming) - .padding() - .overlay { - VStack { - Text(connectionStateMessage) - .font(.system(.caption, design: .rounded)) - .padding(8) - .background( - Capsule(style: .continuous) - .fill(connectionStateFill) - ) - .scaleEffect(viewModel.isStreaming ? 0.5 : 1) - .animation(.default, value: viewModel.isStreaming) - - Spacer() - - GeometryReader { geometry in - HStack(spacing: 24) { - Button( - action: { - Task { - if viewModel.isStreaming { - await viewModel.stopStreaming() - } else { - await viewModel.startStreaming() - } - } - }, - label: { - ZStack { - Circle().stroke(lineWidth: 3).fill(.primary) - - RoundedRectangle( - cornerRadius: viewModel.isStreaming ? 4 : 20, - style: .continuous - ) - .fill(.purple) - .padding(viewModel.isStreaming ? 12 : 3) - .animation(.easeInOut(duration: 0.15), value: viewModel.isStreaming) - } - } - ) - .frame(width: 44, height: 44) - .buttonStyle(.plain) - - Button( - action: { - Task { - await viewModel.sendHierarchy() - } - }, - label: { - Image(systemName: "arrow.up.circle") - .resizable() - .aspectRatio(contentMode: .fit) - } - ) - .frame(width: 33, height: 33) - .buttonStyle(.plain) - } - .padding(.vertical, 8) - .padding(.horizontal) - .background( - Material.ultraThin, - in: RoundedRectangle(cornerRadius: 16, style: .continuous) - ) - .disabled(isConnected ? false : true) - .position(location) - .gesture( - DragGesture() - .onChanged { value in - var newLocation = startLocation ?? location - newLocation.x += value.translation.width - newLocation.y += value.translation.height - location = newLocation - } - .updating($startLocation) { value, startLocation, transaction in - startLocation = startLocation ?? location - } - .onEnded { value in - withAnimation( - .interpolatingSpring(stiffness: 150, damping: 20, initialVelocity: 5) - ) { - location = snapToLocation( - containerSize: geometry.size, - predictedEndLocation: value.predictedEndLocation - ) - } - } - ) - } - } - } - .animation(.default, value: viewModel.connectionState) - .task { - await viewModel.startMultipeerSession() - } - } - - private func snapToLocation( - containerSize: CGSize, - predictedEndLocation: CGPoint - ) -> CGPoint { - let midWidth = containerSize.width / 2 - let midHeight = containerSize.height / 2 - var endLocation: CGPoint = .zero - - //Top Leading - if predictedEndLocation.x < midWidth - && predictedEndLocation.y < midHeight - { - endLocation = CGPoint( - x: 88, - y: 33 - ) - } - //Top Trailing - else if predictedEndLocation.x > midWidth - && predictedEndLocation.y < midHeight - { - endLocation = CGPoint( - x: containerSize.width - 88, - y: 33 - ) - } - //Bottom Leading - else if predictedEndLocation.x < midWidth - && predictedEndLocation.y > midHeight - { - endLocation = CGPoint( - x: 88, - y: containerSize.height - 66 - ) - } - //Bottom Trailing - else if predictedEndLocation.x > midWidth - && predictedEndLocation.y > midHeight - { - endLocation = CGPoint( - x: containerSize.width - 88, - y: containerSize.height - 66 - ) - } - - return endLocation - } -} - -extension RealityCheckConnectView { - public func arView(_ arView: ARView) -> Self { - self.viewModel.arView = arView - return self - } -} - -#if DEBUG - struct ContentView_Previews: PreviewProvider { - static var previews: some View { - Group { - RealityCheckConnectView( - viewModel: withDependencies { - $0.multipeerClient.start = { (_, _, _, _, _) in - AsyncStream.finished - } - } operation: { - ViewModel( - hostName: "MOCKY", - arView: .init(frame: .null) - ) - } - ) - .previewDisplayName(".notConnected") - - RealityCheckConnectView( - viewModel: withDependencies { - $0.multipeerClient.start = { (_, _, _, _, _) in - AsyncStream { - $0.yield(.session(.stateDidChange(.connecting(Peer(displayName: "MOCKYPEER"))))) - } - } - } operation: { - ViewModel( - hostName: "MOCKY", - arView: .init(frame: .null) - ) - } - ) - .previewDisplayName(".connecting") - - RealityCheckConnectView( - viewModel: .init( - hostName: "MOCKY", - arView: .init(frame: .null) - ) - ) - .previewDisplayName(".connected") - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background { - Image("background_preview", bundle: .module) - .resizable() - .aspectRatio(contentMode: .fill) - .ignoresSafeArea() - } - .preferredColorScheme(.light) - } - } -#endif diff --git a/Sources/RealityCheckConnect/AppInfo+DiscoveryInfo.swift b/Sources/RealityCheckConnect_iOS/AppInfo+DiscoveryInfo.swift similarity index 54% rename from Sources/RealityCheckConnect/AppInfo+DiscoveryInfo.swift rename to Sources/RealityCheckConnect_iOS/AppInfo+DiscoveryInfo.swift index 81841ed..1207ab3 100644 --- a/Sources/RealityCheckConnect/AppInfo+DiscoveryInfo.swift +++ b/Sources/RealityCheckConnect_iOS/AppInfo+DiscoveryInfo.swift @@ -1,10 +1,7 @@ +import DeviceKit import Foundation import MultipeerClient -#if os(iOS) - import DeviceKit -#endif - struct AppInfo { static var appName: String? { readFromInfoPlist(withKey: "CFBundleName") @@ -39,35 +36,30 @@ struct AppInfo { } } -#if os(iOS) - extension AppInfo { - static var discoveryInfo: DiscoveryInfo { - var appVersion: String? - if let version = AppInfo.version, - let build = AppInfo.build - { - appVersion = "\(version) (\(build))" - } +extension AppInfo { + static var discoveryInfo: DiscoveryInfo { + var appVersion: String? + if let version = AppInfo.version, + let build = AppInfo.build + { + appVersion = "\(version) (\(build))" + } - var system: String? - if let systemName = Device.current.systemName, - let systemVersion = Device.current.systemVersion - { - system = "\(systemName) \(systemVersion)" - } + var system: String? - return DiscoveryInfo( - appName: AppInfo.appName, - appVersion: appVersion, - device: Device.current.safeDescription, - system: system - ) - } - } -#else - extension AppInfo { - static var discoveryInfo: DiscoveryInfo { - fatalError("DiscoveryInfo is currently only supported on iOS devices") + if let systemName = Device.current.systemName, + let systemVersion = Device.current.systemVersion + { + system = "\(systemName) \(systemVersion)" } + + let device = Device.current.safeDescription + + return DiscoveryInfo( + appName: AppInfo.appName, + appVersion: appVersion, + device: device, + system: system + ) } -#endif +} diff --git a/Sources/RealityCheckConnect_iOS/CGPoint+snapToLocation.swift b/Sources/RealityCheckConnect_iOS/CGPoint+snapToLocation.swift new file mode 100644 index 0000000..f44a8e0 --- /dev/null +++ b/Sources/RealityCheckConnect_iOS/CGPoint+snapToLocation.swift @@ -0,0 +1,49 @@ +import Foundation + +func snapToLocation( + containerSize: CGSize, + predictedEndLocation: CGPoint +) -> CGPoint { + let midWidth = containerSize.width / 2 + let midHeight = containerSize.height / 2 + var endLocation: CGPoint = .zero + + //Top Leading + if predictedEndLocation.x < midWidth + && predictedEndLocation.y < midHeight + { + endLocation = CGPoint( + x: 88, + y: 33 + ) + } + //Top Trailing + else if predictedEndLocation.x > midWidth + && predictedEndLocation.y < midHeight + { + endLocation = CGPoint( + x: containerSize.width - 88, + y: 33 + ) + } + //Bottom Leading + else if predictedEndLocation.x < midWidth + && predictedEndLocation.y > midHeight + { + endLocation = CGPoint( + x: 88, + y: containerSize.height - 66 + ) + } + //Bottom Trailing + else if predictedEndLocation.x > midWidth + && predictedEndLocation.y > midHeight + { + endLocation = CGPoint( + x: containerSize.width - 88, + y: containerSize.height - 66 + ) + } + + return endLocation +} diff --git a/Sources/RealityCheckConnect_iOS/Media.xcassets/Contents.json b/Sources/RealityCheckConnect_iOS/Media.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Sources/RealityCheckConnect_iOS/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/RealityCheckConnect/Media.xcassets/background_preview.imageset/Contents.json b/Sources/RealityCheckConnect_iOS/Media.xcassets/background_preview.imageset/Contents.json similarity index 100% rename from Sources/RealityCheckConnect/Media.xcassets/background_preview.imageset/Contents.json rename to Sources/RealityCheckConnect_iOS/Media.xcassets/background_preview.imageset/Contents.json diff --git a/Sources/RealityCheckConnect/Media.xcassets/background_preview.imageset/background_preview.png b/Sources/RealityCheckConnect_iOS/Media.xcassets/background_preview.imageset/background_preview.png similarity index 100% rename from Sources/RealityCheckConnect/Media.xcassets/background_preview.imageset/background_preview.png rename to Sources/RealityCheckConnect_iOS/Media.xcassets/background_preview.imageset/background_preview.png diff --git a/Sources/RealityCheckConnect/RealityCheckConnectContainer.swift b/Sources/RealityCheckConnect_iOS/RealityCheckConnectContainer.swift similarity index 98% rename from Sources/RealityCheckConnect/RealityCheckConnectContainer.swift rename to Sources/RealityCheckConnect_iOS/RealityCheckConnectContainer.swift index a513251..a9379c8 100644 --- a/Sources/RealityCheckConnect/RealityCheckConnectContainer.swift +++ b/Sources/RealityCheckConnect_iOS/RealityCheckConnectContainer.swift @@ -1,7 +1,7 @@ import RealityKit import SwiftUI -#if os(iOS) +#if os(iOS) && !os(xrOS) /// As it's not possible to deallocate `ARView` properly, we make sure to use the existing instance. /// More info: diff --git a/Sources/RealityCheckConnect/RealityCheckConnectLibraryViewContent.swift b/Sources/RealityCheckConnect_iOS/RealityCheckConnectLibraryViewContent.swift similarity index 97% rename from Sources/RealityCheckConnect/RealityCheckConnectLibraryViewContent.swift rename to Sources/RealityCheckConnect_iOS/RealityCheckConnectLibraryViewContent.swift index ff7f989..8d303e2 100644 --- a/Sources/RealityCheckConnect/RealityCheckConnectLibraryViewContent.swift +++ b/Sources/RealityCheckConnect_iOS/RealityCheckConnectLibraryViewContent.swift @@ -5,7 +5,7 @@ import SwiftUI /// /// The `RealityCheckConnectLibraryViewContent` struct is used to register the `RealityCheckConnectView` as a library item, allowing developers to easily access and use it from the Xcode library panel. It provides a convenient way to add the `RealityCheckConnectView` to their SwiftUI projects via drag and drop . /// -#if os(iOS) +#if os(iOS) && !os(xrOS) public struct RealityCheckConnectLibraryViewContent: LibraryContentProvider { @LibraryContentBuilder diff --git a/Sources/RealityCheckConnect_iOS/RealityCheckConnectView.swift b/Sources/RealityCheckConnect_iOS/RealityCheckConnectView.swift new file mode 100644 index 0000000..f0706e8 --- /dev/null +++ b/Sources/RealityCheckConnect_iOS/RealityCheckConnectView.swift @@ -0,0 +1,241 @@ +import Dependencies +import MultipeerClient +import RealityKit +import SwiftUI + +/** +Represents a SwiftUI view for controlling the RealityCheck connection and exchange of the running AR experience data. + +Use the `RealityCheckConnectView` struct to display the state of the connection and provide a user interface for establishing a connection with **RealityCheck** macOS app. Once integrated, the GUI will allow to connect and exchange AR scene hierarchy data. It utilizes SwiftUI for rendering the user interface. and can be accessed as a `LibraryItem` from the `Library` panel. + +**Usage** + +1. Initialize `RealityCheckConnectView` and provide an optional `ARView` instance to enable hierarchy sending functionality. +2. Add `RealityCheckConnectView` to your SwiftUI view hierarchy. + +**Example** + +Here's an example of how to use `RealityCheckConnectView`: +```swift +var body: some View { + ZStack { + // Other views + RealityCheckConnectView(arView) + } +} +``` + */ +public struct RealityCheckConnectView: View { + @ObservedObject private var viewModel: RealityCheckConnectViewModel + + public init() { + self.viewModel = .init() + } + + public init( + _ arView: ARView + ) { + self.viewModel = .init(arView: arView) + } + + fileprivate init( + viewModel: RealityCheckConnectViewModel + ) { + self.viewModel = viewModel + } + + var connectionStateFill: Color { + switch viewModel.connectionState { + case .notConnected: + return .red + case .connecting: + return .orange + case .connected: + return .green + } + } + + var connectionStateMessage: String { + switch viewModel.connectionState { + case .notConnected: + return "not connected" + case .connecting: + return "connecting" + case .connected: + return viewModel.isStreaming + ? " " + : "connected to: \(viewModel.hostName)" + } + } + + var isConnected: Bool { + switch viewModel.connectionState { + case .notConnected, .connecting: + return false + case .connected: + return true + } + } + + @State var location: CGPoint = CGPoint(x: 88, y: 33) + @GestureState var startLocation: CGPoint? = nil + + public var body: some View { + ///inner corner radius + padding = outer corner radius + RoundedRectangle(cornerRadius: 32, style: .continuous) + .stroke(lineWidth: viewModel.isStreaming ? 0.5 : 3) + .fill(Material.ultraThin) + .animation(.default, value: viewModel.isStreaming) + .padding() + .overlay { + VStack { + Text(connectionStateMessage) + .font(.system(.caption, design: .rounded)) + .padding(8) + .background( + Capsule(style: .continuous) + .fill(connectionStateFill) + ) + .scaleEffect(viewModel.isStreaming ? 0.5 : 1) + .animation(.default, value: viewModel.isStreaming) + + Spacer() + + GeometryReader { geometry in + HStack(spacing: 24) { + Button( + action: { + Task { + if viewModel.isStreaming { + await viewModel.stopVideoStreaming() + } else { + await viewModel.startVideoStreaming() + } + } + }, + label: { + ZStack { + Circle().stroke(lineWidth: 3) + + RoundedRectangle( + cornerRadius: viewModel.isStreaming ? 4 : 20, + style: .continuous + ) + .fill(.purple) + .padding(viewModel.isStreaming ? 12 : 3) + .animation(.easeInOut(duration: 0.15), value: viewModel.isStreaming) + } + } + ) + .frame(width: 44, height: 44) + .buttonStyle(.plain) + + Button( + action: { + Task { + await viewModel.sendMultipeerData() + } + }, + label: { + Image(systemName: "arrow.triangle.2.circlepath") + .resizable() + .aspectRatio(contentMode: .fit) + } + ) + .frame(width: 33, height: 33) + .buttonStyle(.plain) + } + .padding(10) + .background( + Material.ultraThin, + in: RoundedRectangle(cornerRadius: 24, style: .continuous) + ) + .disabled(isConnected ? false : true) + .position(location) + .gesture( + DragGesture() + .onChanged { value in + var newLocation = startLocation ?? location + newLocation.x += value.translation.width + newLocation.y += value.translation.height + location = newLocation + } + .updating($startLocation) { value, startLocation, transaction in + startLocation = startLocation ?? location + } + .onEnded { value in + withAnimation( + .interpolatingSpring(stiffness: 150, damping: 20, initialVelocity: 5) + ) { + location = snapToLocation( + containerSize: geometry.size, + predictedEndLocation: value.predictedEndLocation + ) + } + } + ) + } + } + } + .animation(.default, value: viewModel.connectionState) + } +} + +extension RealityCheckConnectView { + public func arView(_ arView: ARView) -> Self { + self.viewModel.arView = arView + return self + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + Group { + RealityCheckConnectView( + viewModel: withDependencies { + $0.multipeerClient.start = { (_, _, _, _, _) in + AsyncStream.finished + } + } operation: { + RealityCheckConnectViewModel( + hostName: "MOCKY", + arView: .init(frame: .null) + ) + } + ) + .previewDisplayName(".notConnected") + + RealityCheckConnectView( + viewModel: withDependencies { + $0.multipeerClient.start = { (_, _, _, _, _) in + AsyncStream { + $0.yield(.session(.stateDidChange(.connecting(Peer(displayName: "MOCKYPEER"))))) + } + } + } operation: { + RealityCheckConnectViewModel( + hostName: "MOCKY", + arView: .init(frame: .null) + ) + } + ) + .previewDisplayName(".connecting") + + RealityCheckConnectView( + viewModel: .init( + hostName: "MOCKY", + arView: .init(frame: .null) + ) + ) + .previewDisplayName(".connected") + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background { + Image("background_preview", bundle: .module) + .resizable() + .aspectRatio(contentMode: .fill) + .ignoresSafeArea() + } + .preferredColorScheme(.light) + } +} diff --git a/Sources/RealityCheckConnect_iOS/RealityCheckConnectViewModel.swift b/Sources/RealityCheckConnect_iOS/RealityCheckConnectViewModel.swift new file mode 100644 index 0000000..877c232 --- /dev/null +++ b/Sources/RealityCheckConnect_iOS/RealityCheckConnectViewModel.swift @@ -0,0 +1,157 @@ +import Dependencies +import Models +import MultipeerClient +import RealityDumpClient +import RealityKit +import StreamingClient +import SwiftUI + +final class RealityCheckConnectViewModel: ObservableObject { + @Published var connectionState: MultipeerClient.SessionState + @Published var hostName: String + @Published var isStreaming = false + + @Dependency(\.multipeerClient) var multipeerClient + @Dependency(\.realityDump) var realityDump + @Dependency(\.streamingClient) var streamingClient + + var arView: ARView? + private var selectionEntity = ModelEntity( + mesh: .generateSphere(radius: 0.075), + materials: [UnlitMaterial(color: .systemPink)] + ) + + init( + connectionState: MultipeerClient.SessionState = .notConnected, + hostName: String = "...", + arView: ARView? = nil + ) { + self.connectionState = connectionState + self.hostName = hostName + self.arView = arView + Task { + await startMultipeerSession() + } + } +} + +//MARK: - Multipeer +extension RealityCheckConnectViewModel { + func startMultipeerSession() async { + //MARK: Setup + for await action in await multipeerClient.start( + serviceName: "reality-check", + sessionType: .peer, + discoveryInfo: AppInfo.discoveryInfo + ) { + switch action { + case .session(let sessionAction): + switch sessionAction { + case .stateDidChange(let state): + await MainActor.run { + connectionState = state + } + if case .connected = state { + //MARK: Send Hierarchy + await sendMultipeerData() + } + + case .didReceiveData(let data): + //ARView Debug Options + if let debugOptions = try? JSONDecoder() + .decode( + _DebugOptions.self, + from: data + ) + { + await MainActor.run { + arView?.debugOptions = ARView.DebugOptions( + rawValue: debugOptions.rawValue + ) + } + } + //MARK: Entity selection + else if let entitySelection = try? defaultDecoder.decode( + EntitySelection.self, + from: data + ) { + if let entity = await arView? + .findEntityIdentified(targetID: entitySelection.entityID) + { + await MainActor.run { + let parentBounds = entity.visualBounds(relativeTo: nil) + selectionEntity.setParent(entity) + // selectionEntity.setPosition(parentBounds.center, relativeTo: nil) + // selectionEntity.position.y = parentBounds.extents.y + } + } + } else { + fatalError() + } + } + + case .browser(_): + return + + case .advertiser(let advertiserAction): + switch advertiserAction { + case .didReceiveInvitationFromPeer(let peer): + multipeerClient.acceptInvitation() + multipeerClient.stopAdvertisingPeer() + await MainActor.run { + hostName = peer.displayName + } + } + } + } + } + + func sendMultipeerData() async { + guard let arView else { + //FIXME: make a runtime error instead + fatalError("ARView is required in order to be able to send its hierarchy") + } + + let anchors = await arView.scene.anchors.compactMap { $0 } + var identifiableAnchors: [IdentifiableEntity] = [] + var rawDump: [String] = [] + for anchor in anchors { + rawDump.append(String(customDumping: anchor)) + identifiableAnchors.append(await realityDump.identify(anchor)) + } + + let rawData = try! defaultEncoder.encode(rawDump.reduce("", +)) + multipeerClient.send(rawData) + + let arViewData = try! await defaultEncoder.encode( + CodableARView( + arView, + anchors: identifiableAnchors, + contentScaleFactor: arView.contentScaleFactor + ) + ) + multipeerClient.send(arViewData) + } +} + +//MARK: - Video streaming +extension RealityCheckConnectViewModel { + + func startVideoStreaming() async { + await MainActor.run { + isStreaming = true + } + + for await frameData in await streamingClient.startScreenCapture() { + multipeerClient.send(frameData) + } + } + + func stopVideoStreaming() async { + await MainActor.run { + isStreaming = false + } + + streamingClient.stopScreenCapture() + } +} diff --git a/Sources/RealityCheckConnect_visionOS/AppInfo+DiscoveryInfo.swift b/Sources/RealityCheckConnect_visionOS/AppInfo+DiscoveryInfo.swift new file mode 100644 index 0000000..3fde478 --- /dev/null +++ b/Sources/RealityCheckConnect_visionOS/AppInfo+DiscoveryInfo.swift @@ -0,0 +1,62 @@ +import Foundation +import MultipeerClient + +struct AppInfo { + static var appName: String? { + readFromInfoPlist(withKey: "CFBundleName") + } + + static var version: String? { + readFromInfoPlist(withKey: "CFBundleShortVersionString") + } + + static var build: String? { + readFromInfoPlist(withKey: "CFBundleVersion") + } + + static var minimumOSVersion: String? { + readFromInfoPlist(withKey: "MinimumOSVersion") + } + + static var copyrightNotice: String? { + readFromInfoPlist(withKey: "NSHumanReadableCopyright") + } + + static var bundleIdentifier: String? { + readFromInfoPlist(withKey: "CFBundleIdentifier") + } + + // lets hold a reference to the Info.plist of the app as Dictionary + static private let infoPlistDictionary = Bundle.main.infoDictionary + + /// Retrieves and returns associated values (of Type String) from info.Plist of the app. + static private func readFromInfoPlist(withKey key: String) -> String? { + infoPlistDictionary?[key] as? String + } +} + +extension AppInfo { + static var discoveryInfo: DiscoveryInfo { + var appVersion: String? + if let version = AppInfo.version, + let build = AppInfo.build + { + appVersion = "\(version) (\(build))" + } + + var system: String? + + //FIXME: find a way to get the system version + system = "visionOS 1.0" + + //FIXME: find a way to get the device name + let device = "Vision Pro" + + return DiscoveryInfo( + appName: AppInfo.appName, + appVersion: appVersion, + device: device, + system: system + ) + } +} diff --git a/Sources/RealityCheckConnect_visionOS/Extensions/RealityView+ViewModel.swift b/Sources/RealityCheckConnect_visionOS/Extensions/RealityView+ViewModel.swift new file mode 100644 index 0000000..541ba74 --- /dev/null +++ b/Sources/RealityCheckConnect_visionOS/Extensions/RealityView+ViewModel.swift @@ -0,0 +1,28 @@ +import RealityKit +import SwiftUI + +/* +//TODO: This would be significantly more pleasant. + extension RealityView { + public init( + _ realityCheckConnectViewModel: RealityCheckConnectViewModel, + make: @escaping @MainActor @Sendable (inout RealityViewContent) async -> Void, + update: (@MainActor (inout RealityViewContent) -> Void)? = nil + ) where Content == RealityViewContent.Body { + self.init( + make: { @MainActor content in + await make(&content) + realityCheckConnectViewModel.content = content + }, + update: { @MainActor content in + update?(&content) + if case .connected = realityCheckConnectViewModel.connectionState { + Task { [content] in + await realityCheckConnectViewModel.sendMultipeerData(content) + } + } + } + ) + } +} +*/ diff --git a/Sources/RealityCheckConnect_visionOS/Extensions/RealityViewContent+Root.swift b/Sources/RealityCheckConnect_visionOS/Extensions/RealityViewContent+Root.swift new file mode 100644 index 0000000..b75ee65 --- /dev/null +++ b/Sources/RealityCheckConnect_visionOS/Extensions/RealityViewContent+Root.swift @@ -0,0 +1,18 @@ +import Foundation +import RealityKit +import SwiftUI + +extension RealityViewContent { + var root: Entity? { + guard let firstEntity = self.entities.first else { return nil } + return climbToRoot(from: firstEntity) + } + + private func climbToRoot(from entity: Entity) -> Entity { + if let parent = entity.parent { + return climbToRoot(from: parent) + } else { + return entity + } + } +} diff --git a/Sources/RealityCheckConnect_visionOS/RealityCheckConnectViewModel.swift b/Sources/RealityCheckConnect_visionOS/RealityCheckConnectViewModel.swift new file mode 100644 index 0000000..e785151 --- /dev/null +++ b/Sources/RealityCheckConnect_visionOS/RealityCheckConnectViewModel.swift @@ -0,0 +1,158 @@ +import Dependencies +import Foundation +import Models +import MultipeerClient +import RealityKit +import SwiftUI +import StreamingClient +import RealityDump +import RealityDumpClient + +@Observable +final public class RealityCheckConnectViewModel { + var connectionState: MultipeerClient.SessionState + var scenes: [UInt64: RealityViewContent] = [:] + var hostName: String + var isStreaming = false + var selectedEntityID: UInt64? + + public init( + connectionState: MultipeerClient.SessionState = .notConnected, + hostName: String = "..." + ) { + self.connectionState = connectionState + self.hostName = hostName + + Task(priority: .userInitiated) { + await startMultipeerSession() + } + } + + func updateContent(_ content: RealityViewContent) { + guard let scene = content.root?.scene else { return } + scenes.updateValue(content, forKey: scene.id) + Task { + await sendMultipeerData() + } + } +} + +//MARK: - Multipeer +//FIXME: "Extensions must not contain stored properties" error for @Dependency +extension RealityCheckConnectViewModel { + fileprivate func startMultipeerSession() async { + @Dependency(\.multipeerClient) var multipeerClient + + /// Setup + for await action in await multipeerClient.start( + serviceName: "reality-check", + sessionType: .peer, + discoveryInfo: AppInfo.discoveryInfo + ) { + switch action { + case .session(let sessionAction): + switch sessionAction { + case .stateDidChange(let state): + await MainActor.run { + connectionState = state + } + + if case .connected = state { + /// Send Hierarchy + await sendMultipeerData() + } + + case .didReceiveData(let data): + /// Entity selection + if let entitySelection = try? defaultDecoder.decode( + EntitySelection.self, + from: data + ) { + selectedEntityID = entitySelection.entityID + await sendSelectedEntityMultipeerRawData() + } + } + + case .browser(_): + return + + case .advertiser(let advertiserAction): + switch advertiserAction { + case .didReceiveInvitationFromPeer(let peer): + multipeerClient.acceptInvitation() + multipeerClient.stopAdvertisingPeer() + await MainActor.run { + hostName = peer.displayName + } + } + } + } + } + + fileprivate func sendMultipeerData() async { + @Dependency(\.multipeerClient) var multipeerClient + @Dependency(\.realityDump) var realityDump + + guard case .connected = connectionState else { return } + + //TODO: remove/hide reference entity + + //FIXME: improve naming, on visionOS first level children are not anchors + var identifiableAnchors: [IdentifiableEntity] = [] + + for scene in scenes.values { + guard let root = scene.root else { return } + identifiableAnchors.append(await realityDump.identify(root)) + } + + let realityViewData = try! defaultEncoder.encode(CodableScene(anchors: identifiableAnchors)) + multipeerClient.send(realityViewData) + + // TODO: set default selection? + // if selectedEntityID == nil { + // await sendSelectedEntityMultipeerRawData() + // } + } + + fileprivate func sendSelectedEntityMultipeerRawData() async { + @Dependency(\.multipeerClient) var multipeerClient + + guard let selectedEntityID else { return } + + for scene in scenes.values { + guard let root = scene.root else { return } + + if let selectedEntity = findEntity(root: root, targetID: selectedEntityID) { + let rawData = try! defaultEncoder.encode(String(customDumping: selectedEntity)) + multipeerClient.send(rawData) + } + } + } +} + +//MARK: - Video streaming +//FIXME: "Extensions must not contain stored properties" error for @Dependency +extension RealityCheckConnectViewModel { + public func startVideoStreaming() async { + @Dependency(\.multipeerClient) var multipeerClient + @Dependency(\.streamingClient) var streamingClient + + await MainActor.run { + isStreaming = true + } + + for await frameData in await streamingClient.startScreenCapture() { + multipeerClient.send(frameData) + } + } + + func stopVideoStreaming() async { + @Dependency(\.streamingClient) var streamingClient + + await MainActor.run { + isStreaming = false + } + + streamingClient.stopScreenCapture() + } +} diff --git a/Sources/RealityCheckConnect_visionOS/RealityCheckView.swift b/Sources/RealityCheckConnect_visionOS/RealityCheckView.swift new file mode 100644 index 0000000..363a63f --- /dev/null +++ b/Sources/RealityCheckConnect_visionOS/RealityCheckView.swift @@ -0,0 +1,44 @@ +import RealityKit +import SwiftUI + +public struct RealityCheckView: View { + @Environment(RealityCheckConnectViewModel.self) + var realityCheckConnectModel + + let make: @MainActor @Sendable (inout RealityViewContent) async -> Void + var update: ((inout RealityViewContent) -> Void)? + + public init( + make: @escaping @MainActor @Sendable (inout RealityViewContent) async -> Void, + update: ((inout RealityViewContent) -> Void)? = nil + ) { + self.make = make + self.update = update + } + + public var body: some View { + RealityView( + make: { content in + let referenceEntity = Entity() + referenceEntity.name = "__realityCheck" + referenceEntity.isAccessibilityElement = false + referenceEntity.isEnabled = false + content.add(referenceEntity) + await make(&content) + }, + update: { content in + update?(&content) + realityCheckConnectModel.updateContent(content) + } + ) + } +} + +extension View { + public func realityCheck() -> some View { + self + .background { + RealityCheckView { _ in } + } + } +} diff --git a/Sources/RealityDumpClient/Client.swift b/Sources/RealityDumpClient/Client.swift index b737d64..a7f99cb 100644 --- a/Sources/RealityDumpClient/Client.swift +++ b/Sources/RealityDumpClient/Client.swift @@ -3,30 +3,35 @@ import Models import RealityKit public struct RealityDump { - public func raw( - _ loadedEntity: Entity, - printing: Bool = true, - detail: Int = 1, - org: Bool = true - ) async -> [String] { - await self.raw(loadedEntity, printing, detail, org) - } - var raw: (Entity, Bool, Int, Bool) async -> [String] +// @discardableResult +// /// Uses PointFree `custom-dump` to represent textually an `Entity` +// /// - Parameters: +// /// - entity: The entity to instrospect +// /// - printing: Optionally display on console +// /// - Returns: A structured string +// public func dump( +// _ entity: Entity, +// printing: Bool = true +// ) async -> String { +// await self.dump(entity, printing) +// } +// +// var dump: (Entity, Bool) async -> String - public func identify( - _ loadedEntity: Entity, - detail: Int = 1 - ) async -> IdentifiableEntity { - await self.identify(loadedEntity, detail) - } + public func identify( + _ entity: Entity, + detail: Int = 1 + ) async -> IdentifiableEntity { + await self.identify(entity, detail) + } - var identify: (Entity, Int) async -> IdentifiableEntity + var identify: (Entity, Int) async -> IdentifiableEntity } extension DependencyValues { - public var realityDump: RealityDump { - get { self[RealityDump.self] } - set { self[RealityDump.self] = newValue } - } + public var realityDump: RealityDump { + get { self[RealityDump.self] } + set { self[RealityDump.self] = newValue } + } } diff --git a/Sources/RealityDumpClient/Live.swift b/Sources/RealityDumpClient/Live.swift index c215156..b096076 100644 --- a/Sources/RealityDumpClient/Live.swift +++ b/Sources/RealityDumpClient/Live.swift @@ -1,61 +1,65 @@ +import CustomDump import Dependencies import Foundation import Models import RealityKit extension RealityDump: DependencyKey { - public static var liveValue: Self { - return Self( - raw: { (loadedEntity, printing, detail, org) in - dumpRealityEntity(loadedEntity, printing: printing) - }, - identify: { (loadedEntity, detail) in - Parser.identify(loadedEntity) - } - ) - } + public static var liveValue: Self { + return Self( +// dump: { (loadedEntity, printing) in +// //TODO: honor printing parameter +// String(customDumping: loadedEntity) +// }, + identify: { (loadedEntity, detail) in + Parser.identify(loadedEntity) + } + ) + } } // MARK: - enum Parser { - static func identify(_ loadedEntity: Entity, detail: Int = 1) -> IdentifiableEntity { - identifyEntity(loadedEntity, detail: detail, nesting: 1) - } + static func identify(_ loadedEntity: Entity, detail: Int = 1) -> IdentifiableEntity { + identifyEntity(loadedEntity, detail: detail, nesting: 1) + } - private static func identifyComponents( - _ components: Entity.ComponentSet - ) -> [IdentifiableComponent] { - var identifiableComponents: [IdentifiableComponent] = [] - for componentType in IdentifiableComponent.ComponentType.allCases { - if let component = components[componentType.rawValue] { - identifiableComponents.append(IdentifiableComponent(component)) - } - } - return identifiableComponents + private static func identifyComponents( + _ components: Entity.ComponentSet + ) -> [IdentifiableComponent] { + var identifiableComponents: [IdentifiableComponent] = [] + for componentType in IdentifiableComponent.ComponentType.allCases { + if let component = components[componentType.rawValue] { + identifiableComponents.append(IdentifiableComponent(component)) + } } + return identifiableComponents + } - private static func identifyEntity( - _ loadedEntity: Entity, detail: Int, nesting: Int - ) -> IdentifiableEntity { - let state = IdentifiableEntity.State( - isEnabled: loadedEntity.isEnabled, - isEnabledInHierarchy: loadedEntity.isEnabledInHierarchy, - isActive: loadedEntity.isActive, - isAnchored: loadedEntity.isAnchored - ) - let hierarhy = IdentifiableEntity.Hierarhy( - children: loadedEntity.children.compactMap({ identify($0) }), - hasParent: !(loadedEntity.parent == nil) - ) - let components = IdentifiableEntity.Components( - components: identifyComponents(loadedEntity.components) - ) - return IdentifiableEntity( - loadedEntity, - state: state, - hierarhy: hierarhy, - components: components - ) - } + private static func identifyEntity( + _ loadedEntity: Entity, + detail: Int, + nesting: Int + ) -> IdentifiableEntity { + let state = IdentifiableEntity.State( + isEnabled: loadedEntity.isEnabled, + isEnabledInHierarchy: loadedEntity.isEnabledInHierarchy, + isActive: loadedEntity.isActive, + isAnchored: loadedEntity.isAnchored + ) + let hierarhy = IdentifiableEntity.Hierarhy( + children: loadedEntity.children.compactMap({ identify($0) }), + parentID: loadedEntity.parent?.id + ) + let components = IdentifiableEntity.Components( + components: identifyComponents(loadedEntity.components) + ) + return IdentifiableEntity( + loadedEntity, + state: state, + hierarhy: hierarhy, + components: components + ) + } } diff --git a/Sources/RealityDumpClient/RealityDump.swift b/Sources/RealityDumpClient/RealityDump.swift deleted file mode 100644 index 9d876d7..0000000 --- a/Sources/RealityDumpClient/RealityDump.swift +++ /dev/null @@ -1,305 +0,0 @@ -// RealityDump.swift -// Created by Yasuhito NAGATOMO on 2022/02/18. -import Foundation -import RealityKit - -#if DEBUG - - private let keywords = [ // (string, indentLevel [1...]) - ("Entity", 1), - ("ModelEntity", 1), - ("AnchorEntity", 1), - ("Components", 2), - ("Unknown Component", 3), - ("Transform Component", 3), - ("Synchronization Component", 3), - ("Anchoring Component", 3), - ("Model Component", 3), - ("Collision Component", 3), - ("PhysicsBody Component", 3), - ("PhysicsMotion Component", 3), - ("CharacterController Component", 3), - ("CharacterControllerState Component", 3), - ] - - /// Dump the RealityKit Entity object - /// - Parameters: - /// - entity: Entity or ModelEntity or AnchorEntity - /// - printing: if true, strings are printed to the console. - /// - detail: 0 = simple, 1 = detailed - /// - org: true = Emacs org mode - /// - Returns: dumped strings of the entity - @discardableResult - public func dumpRealityEntity( - _ loadedEntity: Entity, printing: Bool = true, detail: Int = 1, org: Bool = true - ) -> [String] { - var strings = [String]() - if org { - strings.append("-*- mode:org -*-") - } - - strings += dumpEntity(loadedEntity, detail: detail, nesting: 0) - - let orgStrings: [String] - if org { - let maxLevel = (keywords.map { $0.1 }.max() ?? 0) + 1 - orgStrings = strings.map { string -> String in - var orgPrefix = "" - let hitKeywords = keywords.compactMap { keyword in - string.contains(keyword.0) ? keyword : nil - } - if let keyword = hitKeywords.first { - orgPrefix = - String(repeating: "*", count: keyword.1) - + String(repeating: " ", count: maxLevel - keyword.1) - } else { - orgPrefix = String(repeating: " ", count: maxLevel) - } - return orgPrefix + string - } - } else { - orgStrings = strings - } - if printing { - orgStrings.forEach { print($0) } - } - return orgStrings - } - - private func dumpEntity(_ loadedEntity: Entity, detail: Int, nesting: Int) -> [String] { - let indentCharacterNumber = 2 - let strings = entityToStrings(loadedEntity, detail: detail, nesting: nesting) - let nestedStrings = strings.map { string -> String in - String(repeating: " ", count: nesting * indentCharacterNumber) + string - } - return nestedStrings - } - - private func entityToStrings(_ loadedEntity: Entity, detail: Int, nesting: Int) -> [String] { - var strings = [String]() - let modelEntity = loadedEntity as? ModelEntity - let anchorEntity = loadedEntity as? AnchorEntity - - if anchorEntity != nil { - strings.append(" \(keywords[2].0)") // AnchorEntity - if let anchorId = anchorEntity?.anchorIdentifier { - strings.append(" +-- anchorIdentifier: \(anchorId)") - } - } else if modelEntity != nil { - strings.append(" \(keywords[1].0)") // ModelEntity - } else { - strings.append("<.> \(keywords[0].0)") // Entity - } - strings.append(" +-- name: \(loadedEntity.name)") - strings.append(" +-- id (Uint64): \(loadedEntity.id)") - strings.append(" +-- State") - strings.append(" | +-- isEnabled: \(loadedEntity.isEnabled)") - strings.append(" | +-- isEnabledInHierarchy: \(loadedEntity.isEnabledInHierarchy)") - strings.append(" | +-- isActive: \(loadedEntity.isActive)") - strings.append(" | +-- isAnchored: \(loadedEntity.isAnchored)") - strings.append(" +-- Animation") - strings.append( - " | +-- number of animations: \(loadedEntity.availableAnimations.count)") - loadedEntity.availableAnimations.forEach { animation in - strings.append(" | +-- name: \(animation.name ?? "(none)")") - } - - if let model = modelEntity { - strings.append(" +-- Joint") - strings.append(" | +-- number of jointNames: \(model.jointNames.count)") - model.jointNames.forEach { jointName in - strings.append(" | +-- jointName: \(jointName)") - } - strings.append(" | +-- number of jointTransforms: \(model.jointTransforms.count)") - model.jointTransforms.forEach { jointTransform in - strings.append(" | +-- jointTransform: \(jointTransform)") - } - } - - strings.append(" +-- Hierarhy") - strings.append(" | +-- has a parent: \(loadedEntity.parent == nil ? "No" : "Yes")") - strings.append(" | +-- number of children: \(loadedEntity.children.count)") - strings.append(" +-- \(keywords[3].0)") // "Components" - strings.append(" | +-- number of components: \(loadedEntity.components.count)") - if loadedEntity.components.count != 0 { - strings += componentsToStrings(loadedEntity.components, detail: detail) - } - // dumpStrings.append(" +-- Description: \(entity.debugDescription)") - strings.append(" +-------------------------------------------------") - - loadedEntity.children.forEach { child in - strings += dumpEntity(child, detail: detail, nesting: nesting + 1) - } - - return strings - } - - private func componentsToStrings(_ components: Entity.ComponentSet, detail: Int) -> [String] { - let componentTypes: [Component.Type] = [ - Transform.self, - SynchronizationComponent.self, - AnchoringComponent.self, - ModelComponent.self, - CollisionComponent.self, - PhysicsBodyComponent.self, - PhysicsMotionComponent.self, - CharacterControllerComponent.self, - CharacterControllerStateComponent.self, - ] - var strings = [String]() - - componentTypes.forEach { type in - if let component = components[type] { - if let theComponent = component as? Transform { - strings += transformComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? SynchronizationComponent { - strings += syncComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? AnchoringComponent { - strings += anchoringComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? ModelComponent { - strings += modelComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? CollisionComponent { - strings += collisionComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? PhysicsBodyComponent { - strings += physicsBodyComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? PhysicsMotionComponent { - strings += physicsMotionComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? CharacterControllerComponent { - strings += characterControllerComponentToStrings(theComponent, detail: detail) - } else if let theComponent = component as? CharacterControllerStateComponent { - strings += characterControllerStateComponentToStrings( - theComponent, detail: detail) - } else { - strings.append(" | +-- \(component)") - } - } - } - if components.count > componentTypes.count { - strings.append(" | +-- \(keywords[4].0)") // "Unknown Component" - } - - return strings - } - - private func transformComponentToStrings(_ component: Transform, detail: Int) -> [String] { - var strings = [String]() - strings.append(" | +-- \(keywords[5].0)") // Transform Component - if detail > 0 { - strings.append(" | | +-- scale: \(component.scale)") - strings.append(" | | +-- rotation: \(component.rotation)") - strings.append(" | | +-- translation: \(component.translation)") - strings.append(" | | +-- matrix: \(component.matrix)") - } - return strings - } - - private func syncComponentToStrings(_ component: SynchronizationComponent, detail: Int) - -> [String] - { - var strings = [String]() - strings.append(" | +-- \(keywords[6].0)") // Synchronization Component - return strings - } - - private func anchoringComponentToStrings(_ component: AnchoringComponent, detail: Int) - -> [String] - { - var strings = [String]() - strings.append(" | +-- \(keywords[7].0)") // "Anchoring Component" - return strings - } - - private func modelComponentToStrings(_ component: ModelComponent, detail: Int) -> [String] { - var strings = [String]() - strings.append(" | +-- \(keywords[8].0)") // "Model Component" - if detail > 0 { - strings.append(" | | +-- bounding Box Margin: \(component.boundsMargin)") - strings.append(" | | +-- mesh") - strings.append( - " | | | +-- expected Material Count: \(component.mesh.expectedMaterialCount)" - ) - strings.append( - " | | | +-- bounding Box - center: \(component.mesh.bounds.center)") - strings.append( - " | | | +-- resource - instances - count : \(component.mesh.contents.instances.count)" - ) - strings.append( - " | | | +-- resource - models - count : \(component.mesh.contents.models.count)" - ) - strings.append(" | | +-- material") - strings.append( - " | | | +-- number of materials: \(component.materials.count)") - component.materials.forEach { material in - strings.append(" | | | +-- material: \(type(of: material))") - } - } - return strings - } - - private func collisionComponentToStrings(_ component: CollisionComponent, detail: Int) - -> [String] - { - var strings = [String]() - strings.append(" | +-- \(keywords[9].0)") // "Collision Component" - if detail > 0 { - strings.append( - " | | +-- number of collision shapes: \(component.shapes.count)") - } - return strings - } - - private func physicsBodyComponentToStrings(_ component: PhysicsBodyComponent, detail: Int) - -> [String] - { - var strings = [String]() - strings.append(" | +-- \(keywords[10].0)") // "PhysicsBody Component" - if detail > 0 { - strings.append( - " | | +-- isContinuousCollisionDetectionEnabled: \(component.isContinuousCollisionDetectionEnabled)" - ) - strings.append(" | | +-- isRotationLocked: \(component.isRotationLocked)") - strings.append(" | | +-- physics body mode: \(component.mode)") - strings.append(" | | +-- mass [kg]: \(component.massProperties.mass)") - strings.append(" | | +-- inertia [kg/m2]: \(component.massProperties.inertia)") - strings.append( - " | | +-- center of mass: \(component.massProperties.centerOfMass)") - } - return strings - } - - private func physicsMotionComponentToStrings(_ component: PhysicsMotionComponent, detail: Int) - -> [String] - { - var strings = [String]() - strings.append(" | +-- \(keywords[11].0)") // "PhysicsMotion Component" - if detail > 0 { - strings.append(" | | +-- anglar velocity: \(component.angularVelocity)") - strings.append(" | | +-- linear velocity: \(component.linearVelocity)") - } - return strings - } - - private func characterControllerComponentToStrings( - _ component: CharacterControllerComponent, detail: Int - ) -> [String] { - var strings = [String]() - strings.append(" | +-- \(keywords[12].0)") // "CharacterController Component" - if detail > 0 { - strings.append(" | | +-- (not be implemented.)") - } - return strings - } - private func characterControllerStateComponentToStrings( - _ component: CharacterControllerStateComponent, detail: Int - ) -> [String] { - var strings = [String]() - strings.append(" | +-- \(keywords[13].0)") // "CharacterControllerState Component" - if detail > 0 { - strings.append(" | | +-- (not be implemented.)") - } - return strings - } -#else - @discardableResult - public func dumpRealityEntity(_ entity: Entity, printing: Bool = true) -> [String] { [] } -#endif diff --git a/Sources/StreamingClient/Client.swift b/Sources/StreamingClient/Client.swift index b9be092..83ad715 100644 --- a/Sources/StreamingClient/Client.swift +++ b/Sources/StreamingClient/Client.swift @@ -4,31 +4,30 @@ import Foundation /// A client for streaming screen capture data and preparing it for rendering. public struct StreamingClient { - /// Starts streaming screen capture data. - /// - /// - Returns: An `AsyncStream` of `Data` representing the screen capture frames. - public var startScreenCapture: () async -> AsyncStream + /// Starts streaming screen capture data. + /// + /// - Returns: An `AsyncStream` of `Data` representing the screen capture frames. + public var startScreenCapture: () async -> AsyncStream - - public var stopScreenCapture: () -> Void + public var stopScreenCapture: () -> Void - /// Prepares the screen capture data for rendering. - /// - /// - Parameter frameData: The `VideoFrameData` to prepare. - public var prepareForRender: (VideoFrameData) -> Void + /// Prepares the screen capture data for rendering. + /// + /// - Parameter frameData: The `VideoFrameData` to prepare. + public var prepareForRender: (VideoFrameData) -> Void - /// Retrieves the next sample for rendering. - /// - /// - Returns: An `AsyncStream` of `Sample` representing the prepared samples for rendering. - public var nextSample: () async -> AsyncStream + /// Retrieves the next sample for rendering. + /// + /// - Returns: An `AsyncStream` of `Sample` representing the prepared samples for rendering. + public var nextSample: () async -> AsyncStream } /// An extension of `DependencyValues` that adds a `streamingClient` property. extension DependencyValues { - /// The `StreamingClient` dependency. - public var streamingClient: StreamingClient { - get { self[StreamingClient.self] } - set { self[StreamingClient.self] = newValue } - } + /// The `StreamingClient` dependency. + public var streamingClient: StreamingClient { + get { self[StreamingClient.self] } + set { self[StreamingClient.self] = newValue } + } } diff --git a/Sources/StreamingClient/StreamingView.swift b/Sources/StreamingClient/MetalViewRepresentable.swift similarity index 95% rename from Sources/StreamingClient/StreamingView.swift rename to Sources/StreamingClient/MetalViewRepresentable.swift index 90d310c..cdc6ff0 100644 --- a/Sources/StreamingClient/StreamingView.swift +++ b/Sources/StreamingClient/MetalViewRepresentable.swift @@ -3,7 +3,7 @@ import MetalKit import SwiftUI #if os(macOS) - public struct StreamingView: NSViewRepresentable { + public struct MetalViewRepresentable: NSViewRepresentable { @Dependency(\.streamingClient) var streamingClient @Binding var viewportSize: CGSize diff --git a/Sources/StreamingClient/Renderer/Renderer.swift b/Sources/StreamingClient/Renderer/Renderer.swift index 5020cbb..16d066f 100644 --- a/Sources/StreamingClient/Renderer/Renderer.swift +++ b/Sources/StreamingClient/Renderer/Renderer.swift @@ -9,196 +9,218 @@ import CoreMedia import Metal import MetalKit -///- Tag: Renderer -class Renderer: NSObject { +#if !os(xrOS) + ///- Tag: Renderer + class Renderer: NSObject { - // Metal objects. - let commandQueue: MTLCommandQueue + // Metal objects. + let commandQueue: MTLCommandQueue - let pipelineStates: PipelineStates - let depthStencilStates: DepthStencilStates + let pipelineStates: PipelineStates + let depthStencilStates: DepthStencilStates - // A buffer that contains image plane vertex data. - let imagePlaneVertexBuffer: BufferView + // A buffer that contains image plane vertex data. + let imagePlaneVertexBuffer: BufferView - var frames = [Frame]() - let frameQueue = DispatchQueue(label: "frameQueue") + var frames = [Frame]() + let frameQueue = DispatchQueue(label: "frameQueue") - // Video texture cache. - let videoTextureCache: CVMetalTextureCache + // Video texture cache. + let videoTextureCache: CVMetalTextureCache - // The current viewport size. - var viewportSize = CGSize() + // The current viewport size. + var viewportSize = CGSize() - var lastDrawnPTS = CMTime.zero - let maxFramesInQueue = 15 + var lastDrawnPTS = CMTime.zero + let maxFramesInQueue = 15 - init(device: MTLDevice, renderDestination: RenderDestination) { + init( + device: MTLDevice, + renderDestination: RenderDestination + ) { - imagePlaneVertexBuffer = BufferView( - device: device, array: Constants.imagePlaneVertexData) + imagePlaneVertexBuffer = BufferView( + device: device, + array: Constants.imagePlaneVertexData + ) - pipelineStates = PipelineStates(device: device, renderDestination: renderDestination) - depthStencilStates = DepthStencilStates(device: device) + pipelineStates = PipelineStates(device: device, renderDestination: renderDestination) + depthStencilStates = DepthStencilStates(device: device) - // Create video texture cache. - var textureCache: CVMetalTextureCache! - CVMetalTextureCacheCreate(nil, nil, device, nil, &textureCache) - videoTextureCache = textureCache + // Create video texture cache. + var textureCache: CVMetalTextureCache! + CVMetalTextureCacheCreate(nil, nil, device, nil, &textureCache) + videoTextureCache = textureCache - // Create the command queue. - commandQueue = device.makeCommandQueue(maxCommandBufferCount: Constants.maxBuffersInFlight)! - } + // Create the command queue. + commandQueue = device.makeCommandQueue(maxCommandBufferCount: Constants.maxBuffersInFlight)! + } - /// Attempts to add a new frame to the queue of frames. - /// If the new frame has a presentation time stamp that is earlier than the lastDrawnPTS, the system discards the frame. - func enqueueFrame( - pixelBuffer: CVPixelBuffer, - presentationTimeStamp: CMTime - ) { + /// Attempts to add a new frame to the queue of frames. + /// If the new frame has a presentation time stamp that is earlier than the lastDrawnPTS, the system discards the frame. + func enqueueFrame( + pixelBuffer: CVPixelBuffer, + presentationTimeStamp: CMTime + ) { - frameQueue.sync { + frameQueue.sync { - /* Frames can back up in the queue due to network conditions. + /* Frames can back up in the queue due to network conditions. If the queue experiences significant backup, the system removes all frames from the queue so that it remains close to live. */ - if frames.count > maxFramesInQueue { - frames.removeAll() - } + if frames.count > maxFramesInQueue { + frames.removeAll() + } - /* There is no purpose for frames that have an earlier presentation + /* There is no purpose for frames that have an earlier presentation time than a previously drawn frame, so the system doesn’t add it to the queue of frames.*/ - guard presentationTimeStamp > lastDrawnPTS else { return } - - // Create two textures (Y and CbCr) from the provided imageBuffer - if CVPixelBufferGetPlaneCount(pixelBuffer) < 2 { return } - - if let videoTextureY = createTexture( - fromPixelBuffer: pixelBuffer, pixelFormat: .r8Unorm, planeIndex: 0), - let videoTextureCbCr = createTexture( - fromPixelBuffer: pixelBuffer, pixelFormat: .rg8Unorm, planeIndex: 1) - { - let newFrame = Frame( - textureY: videoTextureY, - textureCbCr: videoTextureCbCr, - presentationTimeStamp: presentationTimeStamp - ) - - // Insert the newFrame in newest to oldest order according to the presentationTimeStamps. - if let index = frames.firstIndex(where: { (frame) -> Bool in - frame.presentationTimeStamp < newFrame.presentationTimeStamp - }) { - frames.insert(newFrame, at: index) - } else { - frames.append(newFrame) + guard presentationTimeStamp > lastDrawnPTS else { return } + + // Create two textures (Y and CbCr) from the provided imageBuffer + if CVPixelBufferGetPlaneCount(pixelBuffer) < 2 { return } + + if let videoTextureY = createTexture( + fromPixelBuffer: pixelBuffer, + pixelFormat: .r8Unorm, + planeIndex: 0 + ), + let videoTextureCbCr = createTexture( + fromPixelBuffer: pixelBuffer, + pixelFormat: .rg8Unorm, + planeIndex: 1 + ) + { + let newFrame = Frame( + textureY: videoTextureY, + textureCbCr: videoTextureCbCr, + presentationTimeStamp: presentationTimeStamp + ) + + // Insert the newFrame in newest to oldest order according to the presentationTimeStamps. + if let index = frames.firstIndex(where: { (frame) -> Bool in + frame.presentationTimeStamp < newFrame.presentationTimeStamp + }) { + frames.insert(newFrame, at: index) + } else { + frames.append(newFrame) + } } } } - } - private func createTexture( - fromPixelBuffer pixelBuffer: CVPixelBuffer, pixelFormat: MTLPixelFormat, planeIndex: Int - ) -> CVMetalTexture? { - let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex) - let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex) - - var texture: CVMetalTexture? = nil - let status = CVMetalTextureCacheCreateTextureFromImage( - nil, - videoTextureCache, - pixelBuffer, - nil, - pixelFormat, - width, - height, - planeIndex, - &texture) - - if status != kCVReturnSuccess { - texture = nil - } - - return texture - } -} - -// MARK: - MTKViewDelegate -extension Renderer: MTKViewDelegate { + private func createTexture( + fromPixelBuffer pixelBuffer: CVPixelBuffer, + pixelFormat: MTLPixelFormat, + planeIndex: Int + ) -> CVMetalTexture? { + let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex) + let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex) + + var texture: CVMetalTexture? = nil + let status = CVMetalTextureCacheCreateTextureFromImage( + nil, + videoTextureCache, + pixelBuffer, + nil, + pixelFormat, + width, + height, + planeIndex, + &texture + ) + + if status != kCVReturnSuccess { + texture = nil + } - // The system calls this whenever the view changes orientation or the layout changes. - func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { - viewportSize = size + return texture + } } - // The system calls this whenever the view needs to render. - func draw(in view: MTKView) { + // MARK: - MTKViewDelegate + extension Renderer: MTKViewDelegate { - // Only draw if there are available textures. - frameQueue.sync { - guard let frame = frames.popLast() else { return } - - guard let commandBuffer = commandQueue.makeCommandBuffer(), - let renderPassDescriptor = view.currentRenderPassDescriptor - else { return } - - commandBuffer.addCompletedHandler { _ in - withExtendedLifetime(frame) {} - } + // The system calls this whenever the view changes orientation or the layout changes. + func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { + viewportSize = size + } - lastDrawnPTS = frame.presentationTimeStamp + // The system calls this whenever the view needs to render. + func draw(in view: MTKView) { - MetalUtils.encodePass(into: commandBuffer, using: renderPassDescriptor, label: "Main Pass") { - renderEncoder in + // Only draw if there are available textures. + frameQueue.sync { + guard let frame = frames.popLast() else { return } - MetalUtils.encodeStage(using: renderEncoder, label: "Video Stream Stage") { - // Set render command encoder state. - renderEncoder.setCullMode(.none) - renderEncoder.setRenderPipelineState(pipelineStates.videoStream) - renderEncoder.setDepthStencilState(depthStencilStates.videoStream) + guard let commandBuffer = commandQueue.makeCommandBuffer(), + let renderPassDescriptor = view.currentRenderPassDescriptor + else { return } - // Set mesh's vertex buffers. - renderEncoder.setVertexBuffer( - imagePlaneVertexBuffer, offset: 0, - index: BufferIndices.kBufferIndexMeshPositions.rawValue) + commandBuffer.addCompletedHandler { _ in + withExtendedLifetime(frame) {} + } - // Set any textures that the render pipeline reads/samples. - renderEncoder.setFragmentTexture( - CVMetalTextureGetTexture(frame.textureY), index: TextureIndices.kTextureIndexY.rawValue) - renderEncoder.setFragmentTexture( - CVMetalTextureGetTexture(frame.textureCbCr), - index: TextureIndices.kTextureIndexCbCr.rawValue) + lastDrawnPTS = frame.presentationTimeStamp + + MetalUtils.encodePass(into: commandBuffer, using: renderPassDescriptor, label: "Main Pass") + { + renderEncoder in + + MetalUtils.encodeStage(using: renderEncoder, label: "Video Stream Stage") { + // Set render command encoder state. + renderEncoder.setCullMode(.none) + renderEncoder.setRenderPipelineState(pipelineStates.videoStream) + renderEncoder.setDepthStencilState(depthStencilStates.videoStream) + + // Set mesh's vertex buffers. + renderEncoder.setVertexBuffer( + imagePlaneVertexBuffer, + offset: 0, + index: BufferIndices.kBufferIndexMeshPositions.rawValue + ) + + // Set any textures that the render pipeline reads/samples. + renderEncoder.setFragmentTexture( + CVMetalTextureGetTexture(frame.textureY), + index: TextureIndices.kTextureIndexY.rawValue + ) + renderEncoder.setFragmentTexture( + CVMetalTextureGetTexture(frame.textureCbCr), + index: TextureIndices.kTextureIndexCbCr.rawValue + ) + + // Draw each submesh of the mesh. + renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4) + } + } - // Draw each submesh of the mesh. - renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4) + if let currentDrawable = view.currentDrawable { + commandBuffer.present(currentDrawable) } - } - if let currentDrawable = view.currentDrawable { - commandBuffer.present(currentDrawable) + commandBuffer.commit() } - - commandBuffer.commit() } } -} - -// MARK: - Constants -extension Renderer { - enum Constants { - // The maximum number of command buffers in flight. - static let maxBuffersInFlight: Int = 3 - - // Vertex data for an image plane. - static let imagePlaneVertexData: [Float] = [ - -1.0, -1.0, 0.0, 1.0, - 1.0, -1.0, 1.0, 1.0, - -1.0, 1.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 0.0, - ] + + // MARK: - Constants + extension Renderer { + enum Constants { + // The maximum number of command buffers in flight. + static let maxBuffersInFlight: Int = 3 + + // Vertex data for an image plane. + static let imagePlaneVertexData: [Float] = [ + -1.0, -1.0, 0.0, 1.0, + 1.0, -1.0, 1.0, 1.0, + -1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 0.0, + ] + } } -} -struct Frame { - let textureY: CVMetalTexture - let textureCbCr: CVMetalTexture - let presentationTimeStamp: CMTime -} + struct Frame { + let textureY: CVMetalTexture + let textureCbCr: CVMetalTexture + let presentationTimeStamp: CMTime + } +#endif diff --git a/Sources/StreamingClient/Renderer/Utilities/RenderDestination.swift b/Sources/StreamingClient/Renderer/Utilities/RenderDestination.swift index 4292ce2..fa80def 100644 --- a/Sources/StreamingClient/Renderer/Utilities/RenderDestination.swift +++ b/Sources/StreamingClient/Renderer/Utilities/RenderDestination.swift @@ -11,8 +11,10 @@ import MetalKit /// Defines requirements that the renderer expects the render destination to meet. protocol RenderDestination { - var colorPixelFormat: MTLPixelFormat { get set } - var depthStencilPixelFormat: MTLPixelFormat { get set } + var colorPixelFormat: MTLPixelFormat { get set } + var depthStencilPixelFormat: MTLPixelFormat { get set } } -extension MTKView: RenderDestination { } +#if !os(xrOS) + extension MTKView: RenderDestination {} +#endif