@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.
- package/package.json +2 -2
- package/web-spatial/EventEmitter.swift +56 -0
- package/web-spatial/JSBCommand.swift +348 -0
- package/web-spatial/SpatialObject.swift +108 -0
- package/web-spatial/WebMsgCommand.swift +137 -0
- package/web-spatial/WebSpatialApp.swift +111 -0
- package/web-spatial/{libs/uiKitDelegate/Window.swift → Window.swift} +1 -0
- package/web-spatial/manager/Dynamic3DManager.swift +114 -0
- package/web-spatial/manager/JSBManager.swift +148 -0
- package/web-spatial/manager/WKWebViewManager.swift +39 -0
- package/web-spatial/{libs/webView/manifest.swift → manifest.swift} +16 -5
- package/web-spatial/model/SpatialApp.swift +190 -0
- package/web-spatial/model/SpatialScene.swift +1078 -0
- package/web-spatial/model/Spatialized2DElement.swift +128 -0
- package/web-spatial/model/SpatializedDynamic3DElement.swift +34 -0
- package/web-spatial/model/SpatializedElement.swift +95 -0
- package/web-spatial/model/SpatializedStatic3DElement.swift +19 -0
- package/web-spatial/model/dynamic3d/Geometry.swift +95 -0
- package/web-spatial/model/dynamic3d/SpatialComponent.swift +72 -0
- package/web-spatial/model/dynamic3d/SpatialEntity.swift +211 -0
- package/web-spatial/model/dynamic3d/SpatialMaterial.swift +39 -0
- package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +39 -0
- package/web-spatial/model/dynamic3d/SpatialModelResource.swift +38 -0
- package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +18 -0
- package/web-spatial/protocol/ScrollAbleSpatialElementContainer.swift +1 -0
- package/web-spatial/protocol/SpatialScrollAble.swift +8 -0
- package/web-spatial/protocol/SpatializedElementContainer.swift +8 -0
- package/web-spatial/protocol/WebMsgSender.swift +5 -0
- package/web-spatial/types/Vec2.swift +12 -0
- package/web-spatial/types/Vec3.swift +7 -0
- package/web-spatial/view/SceneHandlerUIView.swift +92 -0
- package/web-spatial/{views/ui/NavView.swift → view/SpatialNavView.swift} +149 -77
- package/web-spatial/view/SpatialSceneContentView.swift +218 -0
- package/web-spatial/view/SpatialSceneView.swift +53 -0
- package/web-spatial/view/Spatialized2DElementView.swift +96 -0
- package/web-spatial/view/SpatializedDynamic3DView.swift +173 -0
- package/web-spatial/view/SpatializedElementView.swift +178 -0
- package/web-spatial/view/SpatializedStatic3DView.swift +72 -0
- package/web-spatial/{views → view/view-modifier}/MaterialWithBorderCornerModifier.swift +28 -8
- package/web-spatial/webview/SpatialWebController.swift +300 -0
- package/web-spatial/webview/SpatialWebView.swift +34 -0
- package/web-spatial/webview/SpatialWebViewModel.swift +307 -0
- package/web-spatial.xcodeproj/project.pbxproj +126 -207
- package/web-spatial/libs/EventEmitter.swift +0 -25
- package/web-spatial/libs/SpatialComponent.swift +0 -24
- package/web-spatial/libs/SpatialEntity.swift +0 -172
- package/web-spatial/libs/SpatialInputComponent.swift +0 -19
- package/web-spatial/libs/SpatialMeshResource.swift +0 -12
- package/web-spatial/libs/SpatialModel3DComponent.swift +0 -51
- package/web-spatial/libs/SpatialModelComponent.swift +0 -25
- package/web-spatial/libs/SpatialObject.swift +0 -140
- package/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +0 -19
- package/web-spatial/libs/SpatialViewComponent.swift +0 -8
- package/web-spatial/libs/SpatialWindowComponent.swift +0 -454
- package/web-spatial/libs/SpatialWindowContainer.swift +0 -153
- package/web-spatial/libs/Utils/CommandManager.swift +0 -823
- package/web-spatial/libs/Utils/PerfClock.swift +0 -43
- package/web-spatial/libs/Utils/SceneManager.swift +0 -101
- package/web-spatial/libs/Utils/WindowContainerMgr.swift +0 -122
- package/web-spatial/libs/json/JsonParser.swift +0 -45
- package/web-spatial/libs/webView/UpdateSystem.swift +0 -26
- package/web-spatial/libs/webView/backend/NativeWebView.swift +0 -350
- package/web-spatial/views/ImmersiveView.swift +0 -17
- package/web-spatial/views/OpenDismissHandlerUI.swift +0 -45
- package/web-spatial/views/PlainWindowContainerView.swift +0 -132
- package/web-spatial/views/SpatialModel3DView.swift +0 -187
- package/web-spatial/views/SpatialViewUI.swift +0 -168
- package/web-spatial/views/SpatialWebViewUI.swift +0 -179
- package/web-spatial/views/VolumetricWindowContainerView.swift +0 -30
- package/web-spatial/web_spatialApp.swift +0 -141
- /package/web-spatial/{libs/Utils → Utils}/ColorExtension.swift +0 -0
- /package/web-spatial/{libs/Utils → Utils}/Logger.swift +0 -0
- /package/web-spatial/{views → view}/LoadingView.swift +0 -0
- /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
|
-
}
|