@webspatial/platform-visionos 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/package.json +2 -2
  2. package/web-spatial/EventEmitter.swift +56 -0
  3. package/web-spatial/JSBCommand.swift +348 -0
  4. package/web-spatial/SpatialObject.swift +108 -0
  5. package/web-spatial/WebMsgCommand.swift +137 -0
  6. package/web-spatial/WebSpatialApp.swift +111 -0
  7. package/web-spatial/{libs/uiKitDelegate/Window.swift → Window.swift} +1 -0
  8. package/web-spatial/manager/Dynamic3DManager.swift +114 -0
  9. package/web-spatial/manager/JSBManager.swift +148 -0
  10. package/web-spatial/manager/WKWebViewManager.swift +39 -0
  11. package/web-spatial/{libs/webView/manifest.swift → manifest.swift} +16 -5
  12. package/web-spatial/model/SpatialApp.swift +190 -0
  13. package/web-spatial/model/SpatialScene.swift +1078 -0
  14. package/web-spatial/model/Spatialized2DElement.swift +128 -0
  15. package/web-spatial/model/SpatializedDynamic3DElement.swift +34 -0
  16. package/web-spatial/model/SpatializedElement.swift +95 -0
  17. package/web-spatial/model/SpatializedStatic3DElement.swift +19 -0
  18. package/web-spatial/model/dynamic3d/Geometry.swift +95 -0
  19. package/web-spatial/model/dynamic3d/SpatialComponent.swift +72 -0
  20. package/web-spatial/model/dynamic3d/SpatialEntity.swift +211 -0
  21. package/web-spatial/model/dynamic3d/SpatialMaterial.swift +39 -0
  22. package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +39 -0
  23. package/web-spatial/model/dynamic3d/SpatialModelResource.swift +38 -0
  24. package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +18 -0
  25. package/web-spatial/protocol/ScrollAbleSpatialElementContainer.swift +1 -0
  26. package/web-spatial/protocol/SpatialScrollAble.swift +8 -0
  27. package/web-spatial/protocol/SpatializedElementContainer.swift +8 -0
  28. package/web-spatial/protocol/WebMsgSender.swift +5 -0
  29. package/web-spatial/types/Vec2.swift +12 -0
  30. package/web-spatial/types/Vec3.swift +7 -0
  31. package/web-spatial/view/SceneHandlerUIView.swift +92 -0
  32. package/web-spatial/{views/ui/NavView.swift → view/SpatialNavView.swift} +149 -77
  33. package/web-spatial/view/SpatialSceneContentView.swift +218 -0
  34. package/web-spatial/view/SpatialSceneView.swift +53 -0
  35. package/web-spatial/view/Spatialized2DElementView.swift +96 -0
  36. package/web-spatial/view/SpatializedDynamic3DView.swift +173 -0
  37. package/web-spatial/view/SpatializedElementView.swift +178 -0
  38. package/web-spatial/view/SpatializedStatic3DView.swift +72 -0
  39. package/web-spatial/{views → view/view-modifier}/MaterialWithBorderCornerModifier.swift +28 -8
  40. package/web-spatial/webview/SpatialWebController.swift +300 -0
  41. package/web-spatial/webview/SpatialWebView.swift +34 -0
  42. package/web-spatial/webview/SpatialWebViewModel.swift +307 -0
  43. package/web-spatial.xcodeproj/project.pbxproj +126 -207
  44. package/web-spatial/libs/EventEmitter.swift +0 -25
  45. package/web-spatial/libs/SpatialComponent.swift +0 -24
  46. package/web-spatial/libs/SpatialEntity.swift +0 -172
  47. package/web-spatial/libs/SpatialInputComponent.swift +0 -19
  48. package/web-spatial/libs/SpatialMeshResource.swift +0 -12
  49. package/web-spatial/libs/SpatialModel3DComponent.swift +0 -51
  50. package/web-spatial/libs/SpatialModelComponent.swift +0 -25
  51. package/web-spatial/libs/SpatialObject.swift +0 -140
  52. package/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +0 -19
  53. package/web-spatial/libs/SpatialViewComponent.swift +0 -8
  54. package/web-spatial/libs/SpatialWindowComponent.swift +0 -454
  55. package/web-spatial/libs/SpatialWindowContainer.swift +0 -153
  56. package/web-spatial/libs/Utils/CommandManager.swift +0 -823
  57. package/web-spatial/libs/Utils/PerfClock.swift +0 -43
  58. package/web-spatial/libs/Utils/SceneManager.swift +0 -101
  59. package/web-spatial/libs/Utils/WindowContainerMgr.swift +0 -122
  60. package/web-spatial/libs/json/JsonParser.swift +0 -45
  61. package/web-spatial/libs/webView/UpdateSystem.swift +0 -26
  62. package/web-spatial/libs/webView/backend/NativeWebView.swift +0 -350
  63. package/web-spatial/views/ImmersiveView.swift +0 -17
  64. package/web-spatial/views/OpenDismissHandlerUI.swift +0 -45
  65. package/web-spatial/views/PlainWindowContainerView.swift +0 -132
  66. package/web-spatial/views/SpatialModel3DView.swift +0 -187
  67. package/web-spatial/views/SpatialViewUI.swift +0 -168
  68. package/web-spatial/views/SpatialWebViewUI.swift +0 -179
  69. package/web-spatial/views/VolumetricWindowContainerView.swift +0 -30
  70. package/web-spatial/web_spatialApp.swift +0 -141
  71. /package/web-spatial/{libs/Utils → Utils}/ColorExtension.swift +0 -0
  72. /package/web-spatial/{libs/Utils → Utils}/Logger.swift +0 -0
  73. /package/web-spatial/{views → view}/LoadingView.swift +0 -0
  74. /package/web-spatial/{views → view/view-modifier}/HideViewModifier.swift +0 -0
@@ -1,17 +0,0 @@
1
- import RealityKit
2
- import RealityKitContent
3
- import SwiftUI
4
-
5
- struct ImmersiveView: View {
6
- var body: some View {
7
- RealityView { content in
8
- if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
9
- content.add(scene)
10
- }
11
- }
12
- }
13
- }
14
-
15
- #Preview(immersionStyle: .mixed) {
16
- ImmersiveView()
17
- }
@@ -1,45 +0,0 @@
1
- import SwiftUI
2
-
3
- struct OpenDismissHandlerUI: View {
4
- @Environment(\.openImmersiveSpace) private var openImmersiveSpace
5
- @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace
6
- @Environment(\.openWindow) private var openWindow
7
- @Environment(\.dismissWindow) private var dismissWindow
8
-
9
- @Environment(SpatialWindowContainer.self) var windowContainerContent: SpatialWindowContainer
10
-
11
- @Environment(\.scenePhase) private var scenePhase
12
-
13
- var body: some View {
14
- VStack {}
15
- .onAppear()
16
- .onReceive(windowContainerContent.toggleImmersiveSpace) { v in
17
- if v {
18
- Task {
19
- await openImmersiveSpace(id: "ImmersiveSpace")
20
- }
21
- } else {
22
- Task {
23
- await dismissImmersiveSpace()
24
- }
25
- }
26
- }
27
- .onReceive(windowContainerContent.openWindowData) { wd in
28
- let _ = openWindow(id: wd.windowStyle, value: wd)
29
- }
30
- .onReceive(windowContainerContent.closeWindowData) { wd in
31
- dismissWindow(id: wd.windowStyle, value: wd)
32
- }
33
- .onReceive(windowContainerContent.setLoadingWindowData) { wd in
34
- if wd.method == .show {
35
- openWindow(id: "loading")
36
- } else if wd.method == .hide {
37
- dismissWindow(id: "loading")
38
- }
39
- }
40
-
41
- .onChange(of: scenePhase) { oldValue, newValue in
42
- logger.debug("OpenDismissHandlerUI: Value changed from \(oldValue) to \(newValue)")
43
- }
44
- }
45
- }
@@ -1,132 +0,0 @@
1
- import RealityKit
2
- import SwiftUI
3
-
4
- struct PlainWindowContainerView: View {
5
- @EnvironmentObject private var sceneDelegate: SceneDelegate
6
- @Environment(SpatialWindowContainer.self) private var windowContainerContent: SpatialWindowContainer
7
-
8
- @State private var windowResizeInProgress = false
9
- @State private var timer: Timer?
10
-
11
- private func setSize(size: CGSize) {
12
- sceneDelegate.window?.windowScene?
13
- .requestGeometryUpdate(
14
- .Vision(
15
- size: size
16
- )
17
- )
18
- }
19
-
20
- private func setResizibility(resizingRestrictions: UIWindowScene.ResizingRestrictions) {
21
- sceneDelegate.window?.windowScene?
22
- .requestGeometryUpdate(
23
- .Vision(
24
- resizingRestrictions: resizingRestrictions
25
- )
26
- )
27
- }
28
-
29
- private func setResizeRange(resizeRange: ResizeRange) {
30
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) {
31
- sceneDelegate.window?.windowScene?
32
- .requestGeometryUpdate(
33
- .Vision(
34
- minimumSize: CGSize(
35
- width: resizeRange.minWidth ?? 0,
36
- height: resizeRange
37
- .minHeight ?? 0
38
- ),
39
- maximumSize: CGSize(
40
- width: resizeRange.maxWidth ?? .infinity,
41
- height: resizeRange.maxHeight ?? .infinity
42
- )
43
- )
44
- ) { error in
45
- print("error:", error)
46
- }
47
- }
48
- }
49
-
50
- var body: some View {
51
- OpenDismissHandlerUI().environment(windowContainerContent).onDisappear {
52
- windowContainerContent.destroy()
53
- }
54
-
55
- let rootEntity = windowContainerContent.getEntities().filter {
56
- $0.value.getComponent(SpatialWindowComponent.self) != nil && $0.value.coordinateSpace == .ROOT
57
- }.first?.value
58
-
59
- GeometryReader { proxy3D in
60
- ZStack {
61
- if let e = rootEntity {
62
- let _ = e.forceUpdate ? 0 : 0
63
- let x = proxy3D.size.width / 2
64
- let y = proxy3D.size.height / 2
65
- let z = CGFloat(e.modelEntity.position.z)
66
- let width = proxy3D.size.width
67
- let height = proxy3D.size.height
68
-
69
- if windowResizeInProgress {
70
- VStack {}.frame(width: width, height: height).glassBackgroundEffect().padding3D(.front, -100_000)
71
- .position(x: x, y: y)
72
- .offset(z: z)
73
- } else {
74
- // Avoid showing webview until its loading completes
75
- let wc = e.getComponent(SpatialWindowComponent.self)
76
- let didFinishFirstLoad = wc != nil ? wc!.didFinishFirstLoad : false
77
- SpatialWebViewUI().environment(e)
78
- .frame(width: width, height: height).padding3D(.front, -100_000)
79
- .rotation3DEffect(Rotation3D(simd_quatf(ix: e.modelEntity.orientation.vector.x, iy: e.modelEntity.orientation.vector.y, iz: e.modelEntity.orientation.vector.z, r: e.modelEntity.orientation.vector.w)))
80
- .position(x: x, y: y)
81
- .offset(z: z)
82
- .opacity(didFinishFirstLoad ? 1.0 : 0.0)
83
- .animation(.linear(duration: 0.2), value: didFinishFirstLoad)
84
- .ornament(attachmentAnchor: .scene(.top), contentAlignment: .center) {
85
- if pwaManager.display != .fullscreen {
86
- ZStack {
87
- NavView(swc: wc, navInfo: wc!.navInfo).offset(y: -15)
88
- }.frame(height: 100)
89
- }
90
- }
91
- }
92
- }
93
- }
94
- .onReceive(windowContainerContent.setSize) { newSize in
95
- setSize(size: newSize)
96
- }
97
- .onReceive(windowContainerContent.setResizeRange) { resizeRange in
98
- self.setResizeRange(resizeRange: resizeRange)
99
- }
100
- .onAppear {
101
- let wd = WindowContainerMgr.Instance.getValue()
102
- if let range = wd.resizeRange {
103
- self.setResizeRange(resizeRange: range)
104
- if (range.minWidth != nil || range.minHeight != nil) && range.minWidth == range.maxWidth && range.minHeight == range.maxHeight {
105
- self.setResizibility(resizingRestrictions: .none)
106
- } else {
107
- self.setResizibility(resizingRestrictions: .freeform)
108
- }
109
- }
110
- }
111
- .onChange(of: proxy3D.size) {
112
- // WkWebview has an issue where it doesn't resize while the swift window is resized
113
- // Treid to call didMoveToWindow to force redraw to occur but that seemed to cause rendering artifacts so that solution was rejected
114
- // Now we use a windowResizeInProgress state to hide the webview (by removoving from the view) and other content (using opacity).
115
- // After resize is completed the webview is added back to the page which causes a redraw at the correct dimensions/position
116
- if let wv = rootEntity?.getComponent(SpatialWindowComponent.self) {
117
- windowResizeInProgress = true
118
- if timer != nil {
119
- timer!.invalidate()
120
- }
121
- // If we don't detect resolution change after x seconds we treat the resize as complete
122
- timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
123
- windowResizeInProgress = false
124
- }
125
-
126
- // Trigger resize in the webview's body width and fire a window resize event to get the JS on the page to update state while dragging occurs
127
- wv.evaluateJS(js: "var tempWidth_ = document.body.style.width;document.body.style.width='" + String(Float(proxy3D.size.width)) + "px'; window.dispatchEvent(new Event('resize'));")
128
- }
129
- }
130
- }
131
- }
132
- }
@@ -1,187 +0,0 @@
1
- import RealityKit
2
- import SwiftUI
3
-
4
- class SpatialModel3DViewGestureData {
5
- // for dragging state
6
- var isDragging = false
7
- }
8
-
9
- struct SpatialModel3DView: View {
10
- @Environment(SpatialEntity.self) var e: SpatialEntity
11
- var parentYOffset = Float(0.0)
12
-
13
- @State private var gestureData = SpatialModel3DViewGestureData()
14
-
15
- var drag: some Gesture {
16
- DragGesture()
17
- .onChanged(onDragging)
18
- .onEnded(onDraggingEnded)
19
- }
20
-
21
- var tapGesture: some Gesture {
22
- TapGesture(count: 1)
23
- .onEnded(onTapEnded)
24
- }
25
-
26
- var doubleTapGesture: some Gesture {
27
- TapGesture(count: 2)
28
- .onEnded(onDoubleTapEnded)
29
- }
30
-
31
- var longPressGesture: some Gesture {
32
- LongPressGesture(minimumDuration: 1.0)
33
- .onEnded(onLonePressEnded)
34
- }
35
-
36
- @ViewBuilder
37
- var body: some View {
38
- if e.coordinateSpace == .DOM {
39
- if let childModel3DComponent = e.getComponent(SpatialModel3DComponent.self),
40
- let url = URL(string: childModel3DComponent.modelURL)
41
- {
42
- let x = CGFloat(e.modelEntity.position.x)
43
- let y = CGFloat(e.modelEntity.position.y - (childModel3DComponent.scrollWithParent ? parentYOffset : 0))
44
- let z = CGFloat(e.modelEntity.position.z)
45
- let width = CGFloat(childModel3DComponent.resolutionX)
46
- let height = CGFloat(childModel3DComponent.resolutionY)
47
- let anchor = childModel3DComponent.rotationAnchor
48
- let opacity = childModel3DComponent.opacity
49
- let resizable = childModel3DComponent.resizable
50
- let aspectRatio: CGFloat? = childModel3DComponent.aspectRatio == nil ? nil : CGFloat(childModel3DComponent.aspectRatio!)
51
- let contentMode = childModel3DComponent.contentMode
52
-
53
- let enableTapEvent = childModel3DComponent.enableTapEvent
54
- let enableDoubleTapEvent = childModel3DComponent.enableDoubleTapEvent
55
- let enableDragEvent = childModel3DComponent.enableDragEvent
56
- let enableLongPressEvent = childModel3DComponent.enableLongPressEvent
57
-
58
- // Matrix = MTranslate X MRotate X MScale
59
- Model3D(url: url) { newPhase in
60
- switch newPhase {
61
- case .empty:
62
- ProgressView()
63
-
64
- case let .success(resolvedModel3D):
65
- resolvedModel3D
66
- .resizable(resizable)
67
- .aspectRatio(
68
- aspectRatio,
69
- contentMode: contentMode
70
- )
71
-
72
- .onAppear {
73
- self.onLoadSuccess()
74
- }
75
-
76
- case let .failure(error):
77
- // use UIView.onAppear to notify error phase.
78
- Text("").onAppear {
79
- self.onLoadFailure(error.localizedDescription)
80
- }
81
-
82
- @unknown default:
83
- EmptyView()
84
- }
85
- }
86
- .frame(width: width, height: height)
87
- // .background(Color.blue)
88
- .scaleEffect(
89
- x: CGFloat(e.modelEntity.scale.x),
90
- y: CGFloat(e.modelEntity.scale.y),
91
- z: CGFloat(e.modelEntity.scale.z),
92
- anchor: anchor
93
- )
94
- .rotation3DEffect(
95
- Rotation3D(simd_quatf(
96
- ix: e.modelEntity.orientation.vector.x,
97
- iy: e.modelEntity.orientation.vector.y,
98
- iz: e.modelEntity.orientation.vector.z,
99
- r: e.modelEntity.orientation.vector.w
100
- )),
101
- anchor: anchor
102
- )
103
- .position(x: x, y: y)
104
- .offset(z: z)
105
- .frame(maxDepth: 0, alignment: .back)
106
- .opacity(opacity)
107
- .gesture(enableDragEvent ? drag : nil)
108
- .gesture(enableDoubleTapEvent ?doubleTapGesture : nil)
109
- .gesture(enableTapEvent ? tapGesture : nil)
110
- .gesture(enableLongPressEvent ? longPressGesture : nil)
111
- .hidden(!e.visible)
112
- } else {
113
- Text("").onAppear {
114
- self.onLoadFailure("invalid URL")
115
- }
116
- }
117
- } else {
118
- EmptyView()
119
- }
120
- }
121
-
122
- private func onLoadSuccess() {
123
- if let model3DComponent = e.getComponent(SpatialModel3DComponent.self) {
124
- let data = "{eventType: 'phase', value: 'success'}"
125
- model3DComponent.wv?.fireComponentEvent(componentId: model3DComponent.id, data: data)
126
- }
127
- }
128
-
129
- private func onLoadFailure(_ error: String) {
130
- if let model3DComponent = e.getComponent(SpatialModel3DComponent.self) {
131
- let data = "{eventType: 'phase', value: 'failure', error: '\(error)'} "
132
- model3DComponent.wv?.fireComponentEvent(componentId: model3DComponent.id, data: data)
133
- }
134
- }
135
-
136
- private func onDragging(dragValue: DragGesture.Value) {
137
- var eventType = "drag"
138
- if !gestureData.isDragging {
139
- gestureData.isDragging = true
140
- eventType = "dragstart"
141
- }
142
- fireDragEvent(eventType, dragValue)
143
- }
144
-
145
- private func onDraggingEnded(dragValue: DragGesture.Value) {
146
- gestureData.isDragging = false
147
- let eventType = "dragend"
148
- fireDragEvent(eventType, dragValue)
149
- }
150
-
151
- private func onTapEnded(_: TapGesture.Value) {
152
- print("onTapEnded")
153
- if let model3DComponent = e.getComponent(SpatialModel3DComponent.self) {
154
- let eventType = "tap"
155
- let data = "{eventType: '\(eventType)' } "
156
- model3DComponent.wv?.fireComponentEvent(componentId: model3DComponent.id, data: data)
157
- }
158
- }
159
-
160
- private func onDoubleTapEnded(_: TapGesture.Value) {
161
- print("onDoubleTapEnded")
162
- if let model3DComponent = e.getComponent(SpatialModel3DComponent.self) {
163
- let eventType = "doubletap"
164
- let data = "{eventType: '\(eventType)' } "
165
- model3DComponent.wv?.fireComponentEvent(componentId: model3DComponent.id, data: data)
166
- }
167
- }
168
-
169
- private func onLonePressEnded(_: LongPressGesture.Value) {
170
- print("onLonePressEnded")
171
- if let model3DComponent = e.getComponent(SpatialModel3DComponent.self) {
172
- let eventType = "longpress"
173
- let data = "{eventType: '\(eventType)' } "
174
- model3DComponent.wv?.fireComponentEvent(componentId: model3DComponent.id, data: data)
175
- }
176
- }
177
-
178
- private func fireDragEvent(_ eventType: String, _ value: DragGesture.Value) {
179
- if let model3DComponent = e.getComponent(SpatialModel3DComponent.self) {
180
- let startLocation3D = value.startLocation3D
181
- let translation3D = value.translation3D
182
-
183
- let data = "{eventType: '\(eventType)', value: { translation3D : { x: \(translation3D.x), y: \(translation3D.y), z: \(translation3D.z) }, startLocation3D: { x: \(startLocation3D.x), y: \(startLocation3D.y), z: \(startLocation3D.z)} } } "
184
- model3DComponent.wv?.fireComponentEvent(componentId: model3DComponent.id, data: data)
185
- }
186
- }
187
- }
@@ -1,168 +0,0 @@
1
- //
2
- // SpatialViewUI.swift
3
- // web-spatial
4
- //
5
- // Created by ByteDance on 11/6/24.
6
- //
7
- import RealityKit
8
- import SwiftUI
9
-
10
- extension View {
11
- @ViewBuilder
12
- func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
13
- if condition {
14
- transform(self)
15
- } else {
16
- self
17
- }
18
- }
19
- }
20
-
21
- struct SpatialViewUI: View {
22
- @Environment(SpatialEntity.self) var ent: SpatialEntity
23
- @State var isRoot = false
24
-
25
- // Entity which will contain all the content of this realityView and scale to fit frame
26
- @State var world = Entity()
27
- @State var portal = Entity()
28
- @State var light = PointLight()
29
- @State var portalModel = ModelComponent(
30
- mesh: .generatePlane(width: 1.0, height: 1.0, cornerRadius: 0.0),
31
- materials: [PortalMaterial()]
32
- )
33
- @State var worldComponent = WorldComponent()
34
-
35
- private func toJson(val: SIMD3<Float>) -> String {
36
- return "{x: " + String(val.x) + ",y: " + String(val.y) + ",z: " + String(val.z) + "}"
37
- }
38
-
39
- var dragGesture: some Gesture {
40
- DragGesture(minimumDistance: 0).handActivationBehavior(.automatic)
41
- .targetedToAnyEntity()
42
- .onChanged { value in
43
- let startPos = value.convert(value.startLocation3D, from: .local, to: .scene)
44
- let translate = value.convert(value.location3D, from: .local, to: .scene)
45
- let spatialEntity = value.entity.components[SpatialBridgeComponent.self]!.spatialEntity
46
- let ic = spatialEntity.getComponent(SpatialInputComponent.self)!
47
-
48
- if !ic.isDragging {
49
- ic.isDragging = true
50
- ic.trackedPosition = startPos
51
- let delta = translate - ic.trackedPosition
52
- ic.trackedPosition = translate
53
-
54
- ic.wv!.fireComponentEvent(componentId: ic.id, data: "{eventType: 'dragstart', translate: " + toJson(val: delta) + "}")
55
- } else {
56
- let delta = translate - ic.trackedPosition
57
- ic.trackedPosition = translate
58
- ic.wv!.fireComponentEvent(componentId: ic.id, data: "{eventType: 'drag', translate: " + toJson(val: delta) + "}")
59
- }
60
- }
61
- .onEnded { value in
62
- let spatialEntity = value.entity.components[SpatialBridgeComponent.self]!.spatialEntity
63
- let ic = spatialEntity.getComponent(SpatialInputComponent.self)!
64
- ic.wv!.fireComponentEvent(componentId: ic.id, data: "{eventType: 'dragend'}")
65
- ic.isDragging = false
66
- }
67
- }
68
-
69
- var body: some View {
70
- if let viewComponent = ent.getComponent(SpatialViewComponent.self) {
71
- GeometryReader3D { proxy in
72
- // Get dimensions of the frame
73
- let proxySize3d = proxy.frame(in: .local)
74
-
75
- RealityView { _, _ in
76
- } update: { content, attachments in
77
- // Scale content so it will be a 1x1x1 space and not exceed the frame
78
- let viewSpaceDimensions = content.convert(proxySize3d, from: .local, to: content)
79
- let newScale = min(viewSpaceDimensions.extents.x, viewSpaceDimensions.extents.y)
80
-
81
- world.transform.scale.x = newScale
82
- world.transform.scale.y = newScale
83
- world.transform.scale.z = newScale
84
- portal.transform.scale.x = newScale
85
- portal.transform.scale.y = newScale
86
- portal.transform.scale.z = newScale
87
-
88
- if !isRoot {
89
- // Pull out content so volume sits in front of the page
90
- world.transform.translation.z = world.transform.scale.z / 2
91
- }
92
-
93
- for (_, entity) in ent.getEntities() {
94
- world.addChild(entity.modelEntity)
95
- }
96
-
97
- // Add attachments for window entities
98
- let entities = ent.getEntities().filter { _, entity in
99
- entity.coordinateSpace == .APP && entity.hasComponent(SpatialWindowComponent.self)
100
- }
101
- for key in Array(entities.keys) {
102
- let e = entities[key]!
103
- let windowComponent = e.getComponent(SpatialWindowComponent.self)
104
- if windowComponent != nil && e.coordinateSpace == .APP {
105
- if let windowAttachment = attachments.entity(for: key) {
106
- if e.modelEntity.children.count == 0 {
107
- e.modelEntity.addChild(windowAttachment, preservingWorldTransform: false)
108
-
109
- // Scale the window to fit the resolution to unit ratio as defined by setResolution API
110
- let b = windowAttachment.attachment.bounds
111
- let wv = e.getComponent(SpatialWindowComponent.self)!
112
- let scaleFact = (Float(wv.resolutionX) / 1360.0) / (b.max.x - b.min.x)
113
- windowAttachment.scale.x = scaleFact
114
- windowAttachment.scale.y = scaleFact
115
- windowAttachment.scale.z = scaleFact
116
- }
117
- }
118
- }
119
- }
120
- if viewComponent.isPortal {
121
- // Setup portal
122
- portal.components.set(portalModel)
123
- portal.transform.translation.z = 0.0001 // avoid z fighting
124
- if !portal.components.has(PortalComponent.self) {
125
- portal.components.set(PortalComponent(target: world))
126
- }
127
-
128
- // Setup default light
129
- light.light.intensity = 5000
130
- light.position.z = 2
131
- light.position.y = 1
132
- light.position.x = 0.5
133
- world.addChild(light)
134
-
135
- // Position volume behind portal instead of in front
136
- world.transform.translation.z *= -1
137
-
138
- // Add portal to scene
139
- world.components.set(worldComponent)
140
- content.add(portal)
141
- } else {
142
- // Remove portal elements/components
143
- content.remove(portal)
144
- world.components.remove(WorldComponent.self)
145
- world.removeChild(light)
146
- }
147
-
148
- content.add(world)
149
- }
150
- attachments: {
151
- // Create an attachment for each window component
152
- let entities = ent.getEntities().filter { _, entity in
153
- entity.coordinateSpace == .APP && entity.hasComponent(SpatialWindowComponent.self)
154
- }
155
- ForEach(Array(entities.keys), id: \.self) { key in
156
- let entity = entities[key]!
157
- let wv = entity.getComponent(SpatialWindowComponent.self)!
158
- Attachment(id: key) {
159
- SpatialWebViewUI().environment(entity).frame(width: wv.resolutionX, height: wv.resolutionY)
160
- }
161
- }
162
- }.gesture(dragGesture).if(!isRoot) { view in
163
- view.clipped()
164
- }
165
- }
166
- }
167
- }
168
- }