@webspatial/platform-visionos 1.0.4 → 1.0.5
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 +119 -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 +1042 -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 +104 -0
- package/web-spatial/view/SpatializedElementView.swift +178 -0
- package/web-spatial/view/SpatializedStatic3DView.swift +70 -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
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
import Combine
|
|
2
|
+
import Foundation
|
|
3
|
+
import simd
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
struct CustomReplyData: Codable {
|
|
7
|
+
let type: String
|
|
8
|
+
let name: String
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
struct AddSpatializedElementReply: Codable {
|
|
12
|
+
let id: String
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
struct ResizeRange: Codable {
|
|
16
|
+
var minWidth: Double?
|
|
17
|
+
var minHeight: Double?
|
|
18
|
+
var maxWidth: Double?
|
|
19
|
+
var maxHeight: Double?
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
struct ConvertReply: Codable {
|
|
23
|
+
let id: String
|
|
24
|
+
let position: SIMD3<Float>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let baseReplyData = CustomReplyData(type: "BasicData", name: "jsb call back")
|
|
28
|
+
|
|
29
|
+
let defaultSceneConfig = SceneOptions(
|
|
30
|
+
defaultSize: Size(width: 1280, height: 720),
|
|
31
|
+
windowResizability: .automatic,
|
|
32
|
+
worldScaling: .automatic,
|
|
33
|
+
worldAlignment: .automatic,
|
|
34
|
+
baseplateVisibility: .automatic
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@Observable
|
|
38
|
+
class SpatialScene: SpatialObject, ScrollAbleSpatialElementContainer, WebMsgSender {
|
|
39
|
+
var parent: (any ScrollAbleSpatialElementContainer)?
|
|
40
|
+
|
|
41
|
+
// Enum
|
|
42
|
+
public enum WindowStyle: String, Codable, CaseIterable {
|
|
43
|
+
case window
|
|
44
|
+
case volume
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// TOPIC begin
|
|
48
|
+
var openWindowData = PassthroughSubject<String, Never>()
|
|
49
|
+
var closeWindowData = PassthroughSubject<String, Never>()
|
|
50
|
+
|
|
51
|
+
var setLoadingWindowData = PassthroughSubject<XLoadingViewData, Never>()
|
|
52
|
+
|
|
53
|
+
var url: String = "" // start_url
|
|
54
|
+
public var windowStyle: WindowStyle {
|
|
55
|
+
didSet {
|
|
56
|
+
resetBackgroundMaterialOnWindowStyleChange(windowStyle)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private func resetBackgroundMaterialOnWindowStyleChange(_ windowStyle: WindowStyle) {
|
|
61
|
+
if windowStyle == .volume {
|
|
62
|
+
backgroundMaterial = .Transparent
|
|
63
|
+
} else {
|
|
64
|
+
backgroundMaterial = .None
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
enum SceneStateKind: String {
|
|
69
|
+
// default value
|
|
70
|
+
case idle
|
|
71
|
+
// when SpatialScene is loading
|
|
72
|
+
case pending
|
|
73
|
+
// when SpatialScen will visible after some time
|
|
74
|
+
case willVisible
|
|
75
|
+
// when SpatialScen load Succesfully
|
|
76
|
+
case visible
|
|
77
|
+
// when SpatialScen Failed to load
|
|
78
|
+
case fail
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
var state: SceneStateKind = .idle
|
|
82
|
+
|
|
83
|
+
// TOPIC end
|
|
84
|
+
|
|
85
|
+
var spatialWebViewModel: SpatialWebViewModel
|
|
86
|
+
|
|
87
|
+
init(
|
|
88
|
+
_ url: String,
|
|
89
|
+
_ windowStyle: WindowStyle,
|
|
90
|
+
_ state: SceneStateKind,
|
|
91
|
+
_ sceneOptions: SceneOptions?
|
|
92
|
+
) {
|
|
93
|
+
self.windowStyle = windowStyle
|
|
94
|
+
self.url = url
|
|
95
|
+
spatialWebViewModel = SpatialWebViewModel(url: url)
|
|
96
|
+
super.init()
|
|
97
|
+
resetBackgroundMaterialOnWindowStyleChange(windowStyle)
|
|
98
|
+
|
|
99
|
+
setupSpatialWebView()
|
|
100
|
+
|
|
101
|
+
moveToState(state, sceneOptions)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// used to send message to spatial root webview
|
|
105
|
+
func sendWebMsg(_ id: String, _ msg: Encodable) {
|
|
106
|
+
spatialWebViewModel.sendWebEvent(id, msg)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private func setupSpatialWebView() {
|
|
110
|
+
setupJSBListeners()
|
|
111
|
+
setupWebViewStateListener()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private func handleNavigationCheck(_ url: URL) -> Bool {
|
|
115
|
+
// url in scope should open in place
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private func handleWindowOpenCustom(_ url: URL) -> WebViewElementInfo? {
|
|
120
|
+
// get config from url
|
|
121
|
+
guard let components = URLComponents(string: url.absoluteString),
|
|
122
|
+
let queryItems = components.queryItems
|
|
123
|
+
else {
|
|
124
|
+
print("❌ fail to parse URL")
|
|
125
|
+
return nil
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
guard let encodedUrl = queryItems.first(where: { $0.name == "url" })?.value,
|
|
129
|
+
let decodedUrl = encodedUrl.removingPercentEncoding
|
|
130
|
+
else {
|
|
131
|
+
print("❌ lack of required param url")
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if let encodedConfig = queryItems.first(where: { $0.name == "config" })?.value,
|
|
136
|
+
let decodedConfig = encodedConfig.removingPercentEncoding
|
|
137
|
+
{
|
|
138
|
+
// open new Scene with Config
|
|
139
|
+
let decoder = JSONDecoder()
|
|
140
|
+
guard let configData = decodedConfig.data(using: .utf8) else {
|
|
141
|
+
print("❌ no config key")
|
|
142
|
+
// should not go here
|
|
143
|
+
return nil
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if decodedConfig == "undefined" || decodedConfig == "null" {
|
|
147
|
+
// no scene config, need to create pending SpatialScene
|
|
148
|
+
let newScene = SpatialApp.Instance.createScene(
|
|
149
|
+
decodedUrl,
|
|
150
|
+
.window,
|
|
151
|
+
.pending,
|
|
152
|
+
nil
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return WebViewElementInfo(
|
|
156
|
+
id: newScene.id,
|
|
157
|
+
element: newScene.spatialWebViewModel
|
|
158
|
+
)
|
|
159
|
+
} else {
|
|
160
|
+
do {
|
|
161
|
+
let config: XSceneOptionsJSB = try decoder.decode(XSceneOptionsJSB.self, from: configData)
|
|
162
|
+
|
|
163
|
+
let sceneType = config.type ?? .window
|
|
164
|
+
|
|
165
|
+
let newScene = SpatialApp.Instance.createScene(
|
|
166
|
+
decodedUrl,
|
|
167
|
+
sceneType,
|
|
168
|
+
.willVisible,
|
|
169
|
+
SceneOptions(config)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return WebViewElementInfo(
|
|
173
|
+
id: newScene.id,
|
|
174
|
+
element: newScene.spatialWebViewModel
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
} catch {
|
|
178
|
+
print("❌ config JSON decode fail: \(decodedConfig)")
|
|
179
|
+
return nil
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
} else {
|
|
184
|
+
return nil
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private func handleWindowClose() {
|
|
189
|
+
print("window.close")
|
|
190
|
+
SpatialApp.Instance.closeWindowGroup(self)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public var sceneConfig: SceneOptions?
|
|
194
|
+
|
|
195
|
+
public func moveToState(_ newState: SceneStateKind, _ sceneConfig: SceneOptions?) {
|
|
196
|
+
print(" moveToState \(state) to \(newState) ")
|
|
197
|
+
|
|
198
|
+
let oldState = state
|
|
199
|
+
state = newState
|
|
200
|
+
|
|
201
|
+
if sceneConfig != nil {
|
|
202
|
+
self.sceneConfig = sceneConfig
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if oldState == .idle, newState == .pending {
|
|
206
|
+
SpatialApp.Instance.openLoadingUI(self, true)
|
|
207
|
+
} else if oldState == .pending, newState == .willVisible {
|
|
208
|
+
SpatialApp.Instance.openLoadingUI(self, false)
|
|
209
|
+
// hack to fix windowGroup floating, we need it stay in place of loadingView
|
|
210
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
211
|
+
SpatialApp.Instance
|
|
212
|
+
.openWindowGroup(self, sceneConfig!)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
} else if oldState == .idle, newState == .visible {
|
|
216
|
+
// SpatialApp opened SpatialScene
|
|
217
|
+
} else if oldState == .idle && newState == .willVisible {
|
|
218
|
+
// window.open with scene config
|
|
219
|
+
SpatialApp.Instance.openWindowGroup(self, sceneConfig!)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private func setupJSBListeners() {
|
|
224
|
+
spatialWebViewModel.addJSBListener(GetSpatialSceneStateCommand.self, onGetSpatialSceneState)
|
|
225
|
+
spatialWebViewModel.addJSBListener(InspectCommand.self, onInspect)
|
|
226
|
+
spatialWebViewModel.addJSBListener(UpdateSceneConfigCommand.self, onUpdateSceneConfig)
|
|
227
|
+
spatialWebViewModel
|
|
228
|
+
.addJSBListener(
|
|
229
|
+
FocusSceneCommand.self,
|
|
230
|
+
onFocusScene
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
spatialWebViewModel.addJSBListener(DestroyCommand.self, onDestroySpatialObjectCommand)
|
|
234
|
+
|
|
235
|
+
spatialWebViewModel.addJSBListener(UpdateSpatialSceneProperties.self, onUpdateSpatialSceneProperties)
|
|
236
|
+
|
|
237
|
+
spatialWebViewModel.addJSBListener(AddSpatializedElementToSpatialScene.self, onAddSpatializedElement)
|
|
238
|
+
|
|
239
|
+
spatialWebViewModel.addJSBListener(UpdateSpatialized2DElementProperties.self, onUpdateSpatialized2DElementProperties)
|
|
240
|
+
|
|
241
|
+
spatialWebViewModel.addJSBListener(UpdateSpatializedElementTransform.self, onUpdateSpatializedElementTransform)
|
|
242
|
+
|
|
243
|
+
spatialWebViewModel.addJSBListener(AddSpatializedElementToSpatialized2DElement.self, onAddSpatializedElementToSpatialized2DElement)
|
|
244
|
+
|
|
245
|
+
spatialWebViewModel.addJSBListener(UpdateSpatializedStatic3DElementProperties.self, onUpdateSpatializedStatic3DElementProperties)
|
|
246
|
+
|
|
247
|
+
spatialWebViewModel.addJSBListener(CreateSpatializedStatic3DElement.self, onCreateSpatializedStatic3DElement)
|
|
248
|
+
|
|
249
|
+
spatialWebViewModel.addJSBListener(CreateSpatializedDynamic3DElement.self, onCreateSpatializedDynamic3DElement)
|
|
250
|
+
spatialWebViewModel.addJSBListener(UpdateSpatializedDynamic3DElementProperties.self, onUpdateSpatializedDynamic3DElementProperties)
|
|
251
|
+
spatialWebViewModel.addJSBListener(CreateSpatialEntity.self, onCreateEntity)
|
|
252
|
+
spatialWebViewModel.addJSBListener(CreateGeometryProperties.self, onCreateGeometry)
|
|
253
|
+
spatialWebViewModel.addJSBListener(CreateUnlitMaterial.self, onCreateUnlitMaterial)
|
|
254
|
+
spatialWebViewModel.addJSBListener(CreateModelComponent.self, onCreateModelComponent)
|
|
255
|
+
spatialWebViewModel.addJSBListener(AddComponentToEntity.self, onAddComponentToEntity)
|
|
256
|
+
spatialWebViewModel.addJSBListener(AddEntityToDynamic3D.self, onAddEntityToDynamic3D)
|
|
257
|
+
spatialWebViewModel.addJSBListener(AddEntityToEntity.self, onAddEntityToEntity)
|
|
258
|
+
spatialWebViewModel.addJSBListener(SetParentForEntity.self, onSetParentForEntity)
|
|
259
|
+
spatialWebViewModel.addJSBListener(RemoveEntityFromParent.self, onRemoveEntityFromParent)
|
|
260
|
+
spatialWebViewModel.addJSBListener(UpdateEntityProperties.self, onUpdateEntityProperties)
|
|
261
|
+
spatialWebViewModel.addJSBListener(CreateModelAsset.self, onCreateModelAsset)
|
|
262
|
+
spatialWebViewModel.addJSBListener(CreateSpatialModelEntity.self, onCreateSpatialModelEntity)
|
|
263
|
+
spatialWebViewModel.addJSBListener(UpdateEntityEvent.self, onUpdateEntityEvent)
|
|
264
|
+
spatialWebViewModel.addJSBListener(ConvertFromEntityToEntity.self, onConvertFromEntityToEntity)
|
|
265
|
+
spatialWebViewModel.addJSBListener(ConvertFromEntityToScene.self, onConvertFromEntityToScene)
|
|
266
|
+
spatialWebViewModel.addJSBListener(ConvertFromSceneToEntity.self, onConvertFromSceneToEntity)
|
|
267
|
+
|
|
268
|
+
spatialWebViewModel.addOpenWindowListener(protocal: "webspatial", onOpenWindowHandler)
|
|
269
|
+
|
|
270
|
+
spatialWebViewModel
|
|
271
|
+
.addNavigationListener(protocal: SpatialApp.Instance.scope, event: handleNavigationCheck)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
var width: Double = 0
|
|
275
|
+
|
|
276
|
+
var height: Double = 0
|
|
277
|
+
|
|
278
|
+
var depth: Double = 0
|
|
279
|
+
|
|
280
|
+
static let navHeight = 60.0
|
|
281
|
+
|
|
282
|
+
func updateSize3D(_ size: Size3D) {
|
|
283
|
+
width = size.width
|
|
284
|
+
height = size.height
|
|
285
|
+
depth = size.depth
|
|
286
|
+
|
|
287
|
+
// write through
|
|
288
|
+
spatialWebViewModel.updateWindowKV([
|
|
289
|
+
"innerDepth": depth,
|
|
290
|
+
"outerDepth": depth,
|
|
291
|
+
"outerHeight": height + SpatialScene.navHeight,
|
|
292
|
+
])
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public var didFailLoad = false
|
|
296
|
+
|
|
297
|
+
private func setupWebViewStateListener() {
|
|
298
|
+
spatialWebViewModel.addStateListener(.didStartLoad) {
|
|
299
|
+
self.didFailLoad = false
|
|
300
|
+
self.onPageStartLoad()
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
spatialWebViewModel.addScrollUpdateListener { _, point in
|
|
304
|
+
self._scrollOffset.x = point.x
|
|
305
|
+
self._scrollOffset.y = point.y
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
spatialWebViewModel.addStateListener(.didClose) {
|
|
309
|
+
self.handleWindowClose()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
spatialWebViewModel.addStateListener(.didFailLoad) {
|
|
313
|
+
self.didFailLoad = true
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
spatialWebViewModel.addStateListener(.didFinishLoad) {
|
|
317
|
+
if self.state == .pending {
|
|
318
|
+
self.checkHookExist()
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private func checkHookExist(_ completion: ((Bool) -> Void)? = nil) {
|
|
324
|
+
let js = """
|
|
325
|
+
(function() {
|
|
326
|
+
return typeof window.xrCurrentSceneDefaults !== 'undefined';
|
|
327
|
+
})();
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
spatialWebViewModel.evaluateJS(js) { result in
|
|
331
|
+
let exists = result as? Bool ?? false
|
|
332
|
+
|
|
333
|
+
if let completion = completion {
|
|
334
|
+
completion(exists)
|
|
335
|
+
} else {
|
|
336
|
+
if !exists {
|
|
337
|
+
self.moveToState(.willVisible, defaultSceneConfig)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private func onOpenWindowHandler(url: URL) -> WebViewElementInfo? {
|
|
344
|
+
let host = url.host ?? ""
|
|
345
|
+
if host == "createSpatialScene" {
|
|
346
|
+
return handleWindowOpenCustom(url)
|
|
347
|
+
} else {
|
|
348
|
+
let spatialized2DElement: Spatialized2DElement = createSpatializedElement(
|
|
349
|
+
.Spatialized2DElement
|
|
350
|
+
)
|
|
351
|
+
return WebViewElementInfo(id: spatialized2DElement.id, element: spatialized2DElement.getWebViewModel())
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private func onPageStartLoad() {
|
|
356
|
+
// destroy all SpatialObject asset
|
|
357
|
+
let spatialObjectArray = spatialObjects.map { $0.value }
|
|
358
|
+
for spatialObject in spatialObjectArray {
|
|
359
|
+
spatialObject.destroy()
|
|
360
|
+
}
|
|
361
|
+
backgroundMaterial = .None
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private func onGetSpatialSceneState(
|
|
365
|
+
command: GetSpatialSceneStateCommand,
|
|
366
|
+
resolve: @escaping JSBManager.ResolveHandler<Encodable>
|
|
367
|
+
) {
|
|
368
|
+
resolve(.success(CustomReplyData(type: "state", name: state.rawValue)))
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private func onInspect(command: InspectCommand, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
372
|
+
if let targetId = command.id, !targetId.isEmpty {
|
|
373
|
+
if let spatialObject: SpatialObject = findSpatialObject(targetId) {
|
|
374
|
+
resolve(.success(spatialObject))
|
|
375
|
+
return
|
|
376
|
+
} else if let spatialEntity: SpatialEntity = findSpatialObject(targetId) {
|
|
377
|
+
resolve(.success(spatialEntity))
|
|
378
|
+
return
|
|
379
|
+
} else {
|
|
380
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid inspect spatial object id not exsit!")))
|
|
381
|
+
return
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
// inspect current SpatialScene
|
|
385
|
+
resolve(.success(self))
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private func onDestroySpatialObjectCommand(command: DestroyCommand, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
391
|
+
if let spatialObject: SpatialObject = findSpatialObject(command.id) {
|
|
392
|
+
spatialObject.destroy()
|
|
393
|
+
resolve(.success(nil))
|
|
394
|
+
return
|
|
395
|
+
} else if let spatialEntity: SpatialEntity = findSpatialObject(command.id) {
|
|
396
|
+
spatialEntity.destroy()
|
|
397
|
+
resolve(.success(nil))
|
|
398
|
+
return
|
|
399
|
+
} else {
|
|
400
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Failed to destroy SpatialObject: invalid inspect spatial object id \(command.id) not exsit!")))
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private func onCreateSpatializedStatic3DElement(command: CreateSpatializedStatic3DElement, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
406
|
+
let spatialObject: SpatializedStatic3DElement = createSpatializedElement(.SpatializedStatic3DElement)
|
|
407
|
+
spatialObject.modelURL = command.modelURL
|
|
408
|
+
|
|
409
|
+
resolve(.success(AddSpatializedElementReply(id: spatialObject.id)))
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private func onCreateSpatializedDynamic3DElement(command: CreateSpatializedDynamic3DElement, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
413
|
+
let spatialObject: SpatializedDynamic3DElement = createSpatializedElement(.SpatializedDynamic3DElement)
|
|
414
|
+
|
|
415
|
+
resolve(.success(AddSpatializedElementReply(id: spatialObject.id)))
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private func onUpdateSpatializedDynamic3DElementProperties(command: UpdateSpatializedDynamic3DElementProperties, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
419
|
+
guard let spatializedElement: SpatializedDynamic3DElement = findSpatialObject(command.id) else {
|
|
420
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid updateSpatializedDynamic3DElement spatial object id not exsit!")))
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
updateSpatializedElementProperties(spatializedElement, command)
|
|
424
|
+
resolve(.success(baseReplyData))
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private func onUpdateSpatialSceneProperties(command: UpdateSpatialSceneProperties, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
428
|
+
if let material = command.material {
|
|
429
|
+
backgroundMaterial = material
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if let cornerRadius = command.cornerRadius {
|
|
433
|
+
self.cornerRadius = cornerRadius
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if let opacity = command.opacity {
|
|
437
|
+
self.opacity = opacity
|
|
438
|
+
}
|
|
439
|
+
resolve(.success(baseReplyData))
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private func onUpdateSpatializedStatic3DElementProperties(command: UpdateSpatializedStatic3DElementProperties, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
443
|
+
guard let spatializedElement: SpatializedStatic3DElement = findSpatialObject(command.id) else {
|
|
444
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid updateSpatializedStatic3DElement spatial object id not exsit!")))
|
|
445
|
+
return
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
updateSpatializedElementProperties(spatializedElement, command)
|
|
449
|
+
|
|
450
|
+
if let modelURL = command.modelURL {
|
|
451
|
+
spatializedElement.modelURL = modelURL
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if let array = command.modelTransform {
|
|
455
|
+
guard array.count == 16 else {
|
|
456
|
+
print("Received modelTransform matrix array does not have 16 elements.")
|
|
457
|
+
return resolve(.failure(JsbError(code: .InvalidMatrix, message: "invalid UpdateSpatializedStatic3DElementProperties matrix should have length 16!")))
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
let column0 = simd_double4(array[0], array[1], array[2], array[3])
|
|
461
|
+
let column1 = simd_double4(array[4], array[5], array[6], array[7])
|
|
462
|
+
let column2 = simd_double4(array[8], array[9], array[10], array[11])
|
|
463
|
+
let column3 = simd_double4(array[12], array[13], array[14], array[15])
|
|
464
|
+
let simd_double4x4 = simd_double4x4(columns: (column0, column1, column2, column3))
|
|
465
|
+
let affineTransform3D = AffineTransform3D(truncating: simd_double4x4)
|
|
466
|
+
spatializedElement.modelTransform = affineTransform3D
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
resolve(.success(baseReplyData))
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private func onFocusScene(
|
|
473
|
+
command: FocusSceneCommand,
|
|
474
|
+
resolve: @escaping JSBManager.ResolveHandler<Encodable>
|
|
475
|
+
) {
|
|
476
|
+
let sceneId = command.id
|
|
477
|
+
print("onFocusScene \(sceneId)")
|
|
478
|
+
|
|
479
|
+
if let targetScene = SpatialApp.Instance.getScene(sceneId) {
|
|
480
|
+
SpatialApp.Instance.focusScene(targetScene)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
resolve(.success(baseReplyData))
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private func onUpdateSceneConfig(command: UpdateSceneConfigCommand, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
487
|
+
if state == .visible || state == .willVisible {
|
|
488
|
+
print("forbidden to update scene config after visible")
|
|
489
|
+
// prevent re-enter
|
|
490
|
+
resolve(.success(baseReplyData))
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
let sceneConfigJSBData = command.config
|
|
495
|
+
print("onUpdateSceneConfig \(command.config)")
|
|
496
|
+
|
|
497
|
+
// find scene
|
|
498
|
+
let sceneConfig = SceneOptions(sceneConfigJSBData)
|
|
499
|
+
|
|
500
|
+
// update sceneType
|
|
501
|
+
windowStyle = sceneConfigJSBData.type!
|
|
502
|
+
|
|
503
|
+
moveToState(.willVisible, sceneConfig)
|
|
504
|
+
|
|
505
|
+
resolve(.success(baseReplyData))
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private func onAddSpatializedElementToSpatialized2DElement(command: AddSpatializedElementToSpatialized2DElement, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
509
|
+
guard let spatialized2DElement: Spatialized2DElement = findSpatialObject(command.id)
|
|
510
|
+
else {
|
|
511
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid AddSpatializedElementToSpatialized2DElement spatial object id not exsit!")))
|
|
512
|
+
return
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
guard let targetSpatializedElement: SpatializedElement = findSpatialObject(command.spatializedElementId) else {
|
|
516
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid AddSpatializedElementToSpatialized2DElement target spatial object id not exsit!")))
|
|
517
|
+
return
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
targetSpatializedElement.setParent(spatialized2DElement)
|
|
521
|
+
resolve(.success(baseReplyData))
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private func onUpdateSpatialized2DElementProperties(command: UpdateSpatialized2DElementProperties, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
525
|
+
guard let spatialized2DElement: Spatialized2DElement = findSpatialObject(command.id) else {
|
|
526
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid updateSpatializedElementProperties spatial object id not exsit!")))
|
|
527
|
+
return
|
|
528
|
+
}
|
|
529
|
+
updateSpatializedElementProperties(spatialized2DElement, command)
|
|
530
|
+
if let scrollPageEnabled = command.scrollPageEnabled {
|
|
531
|
+
spatialized2DElement.scrollPageEnabled = scrollPageEnabled
|
|
532
|
+
}
|
|
533
|
+
if let material = command.material {
|
|
534
|
+
spatialized2DElement.backgroundMaterial = material
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if let cornerRadius = command.cornerRadius {
|
|
538
|
+
spatialized2DElement.cornerRadius = cornerRadius
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if let scrollEdgeInsetsMarginRight = command.scrollEdgeInsetsMarginRight {
|
|
542
|
+
spatialized2DElement.scrollEdgeInsetsMarginRight = scrollEdgeInsetsMarginRight
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
resolve(.success(baseReplyData))
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
private func updateSpatializedElementProperties(_ spatializedElement: SpatializedElement, _ command: SpatializedElementProperties) {
|
|
549
|
+
if let name = command.name {
|
|
550
|
+
spatializedElement.name = name
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if let clientX = command.clientX {
|
|
554
|
+
spatializedElement.clientX = clientX
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if let clientY = command.clientY {
|
|
558
|
+
spatializedElement.clientY = clientY
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if let width = command.width {
|
|
562
|
+
spatializedElement.width = width
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if let height = command.height {
|
|
566
|
+
spatializedElement.height = height
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if let depth = command.depth {
|
|
570
|
+
spatializedElement.depth = depth
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if let backOffset = command.backOffset {
|
|
574
|
+
spatializedElement.backOffset = backOffset
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if let opacity = command.opacity {
|
|
578
|
+
spatializedElement.opacity = opacity
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if let scrollWithParent = command.scrollWithParent {
|
|
582
|
+
spatializedElement.scrollWithParent = scrollWithParent
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if let visible = command.visible {
|
|
586
|
+
spatializedElement.visible = visible
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if let zIndex = command.zIndex {
|
|
590
|
+
spatializedElement.zIndex = zIndex
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if let rotationAnchor = command.rotationAnchor {
|
|
594
|
+
spatializedElement.rotationAnchor = .init(x: CGFloat(rotationAnchor.x), y: CGFloat(rotationAnchor.y), z: CGFloat(rotationAnchor.z))
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if let enableTapGesture = command.enableTapGesture {
|
|
598
|
+
spatializedElement.enableTapGesture = enableTapGesture
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if let enableDragStartGesture = command.enableDragStartGesture {
|
|
602
|
+
spatializedElement.enableDragStartGesture = enableDragStartGesture
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if let enableDragGesture = command.enableDragGesture {
|
|
606
|
+
spatializedElement.enableDragGesture = enableDragGesture
|
|
607
|
+
}
|
|
608
|
+
if let enableDragEndGesture = command.enableDragEndGesture {
|
|
609
|
+
spatializedElement.enableDragEndGesture = enableDragEndGesture
|
|
610
|
+
}
|
|
611
|
+
if let enableRotateStartGesture = command.enableRotateStartGesture {
|
|
612
|
+
spatializedElement.enableRotateStartGesture = enableRotateStartGesture
|
|
613
|
+
}
|
|
614
|
+
if let enableRotateGesture = command.enableRotateGesture {
|
|
615
|
+
spatializedElement.enableRotateGesture = enableRotateGesture
|
|
616
|
+
}
|
|
617
|
+
if let enableRotateEndGesture = command.enableRotateEndGesture {
|
|
618
|
+
spatializedElement.enableRotateEndGesture = enableRotateEndGesture
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if let enableMagnifyStartGesture = command.enableMagnifyStartGesture {
|
|
622
|
+
spatializedElement.enableMagnifyStartGesture = enableMagnifyStartGesture
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if let enableMagnifyGesture = command.enableMagnifyGesture {
|
|
626
|
+
spatializedElement.enableMagnifyGesture = enableMagnifyGesture
|
|
627
|
+
}
|
|
628
|
+
if let enableMagnifyEndGesture = command.enableMagnifyEndGesture {
|
|
629
|
+
spatializedElement.enableMagnifyEndGesture = enableMagnifyEndGesture
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
private func onUpdateSpatializedElementTransform(command: UpdateSpatializedElementTransform, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
634
|
+
guard let spatializedElement: SpatializedElement = findSpatialObject(command.id) else {
|
|
635
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid UpdateSpatializedElementTransform spatial object id not exsit!")))
|
|
636
|
+
return
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
let array = command.matrix
|
|
640
|
+
|
|
641
|
+
guard array.count == 16 else {
|
|
642
|
+
print("Received matrix array does not have 16 elements.")
|
|
643
|
+
return resolve(.failure(JsbError(code: .InvalidMatrix, message: "invalid UpdateSpatializedElementTransform matrix should have length 16!")))
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
let column0 = simd_double4(array[0], array[1], array[2], array[3])
|
|
647
|
+
let column1 = simd_double4(array[4], array[5], array[6], array[7])
|
|
648
|
+
let column2 = simd_double4(array[8], array[9], array[10], array[11])
|
|
649
|
+
let column3 = simd_double4(array[12], array[13], array[14], array[15])
|
|
650
|
+
let simd_double4x4 = simd_double4x4(columns: (column0, column1, column2, column3))
|
|
651
|
+
let affineTransform3D = AffineTransform3D(truncating: simd_double4x4)
|
|
652
|
+
spatializedElement.transform = affineTransform3D
|
|
653
|
+
|
|
654
|
+
resolve(.success(baseReplyData))
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private func onAddSpatializedElement(command: AddSpatializedElementToSpatialScene, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
658
|
+
guard let spatializedElement: SpatializedElement = findSpatialObject(command.spatializedElementId) else {
|
|
659
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "invalid addSpatializedElementCommand spatial object id not exsit!")))
|
|
660
|
+
return
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
spatializedElement.setParent(self)
|
|
664
|
+
resolve(.success(baseReplyData))
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/*
|
|
668
|
+
* Begin Implement SpatializedElementContainer Protocol
|
|
669
|
+
*/
|
|
670
|
+
|
|
671
|
+
// SpatialScene can hold a collection of SpatializedElement children
|
|
672
|
+
private var children = [String: SpatializedElement]()
|
|
673
|
+
|
|
674
|
+
// Called by SpatializedElement.setParent
|
|
675
|
+
func addChild(_ spatializedElement: SpatializedElement) {
|
|
676
|
+
children[spatializedElement.id] = spatializedElement
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Called by SpatializedElement.setParent
|
|
680
|
+
func removeChild(_ spatializedElement: SpatializedElement) {
|
|
681
|
+
children.removeValue(forKey: spatializedElement.id)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
func getChildrenOfType(_ type: SpatializedElementType) -> [String: SpatializedElement] {
|
|
685
|
+
let typedChildren = children.filter {
|
|
686
|
+
switch type {
|
|
687
|
+
case .Spatialized2DElement:
|
|
688
|
+
return $0.value is Spatialized2DElement
|
|
689
|
+
case .SpatializedStatic3DElement:
|
|
690
|
+
return $0.value is SpatializedStatic3DElement
|
|
691
|
+
case .SpatializedDynamic3DElement:
|
|
692
|
+
return $0.value is SpatializedDynamic3DElement
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return typedChildren
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
func getChildren() -> [String: SpatializedElement] {
|
|
699
|
+
return children
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/*
|
|
703
|
+
* End Implement SpatializedElementContainer Protocol
|
|
704
|
+
*/
|
|
705
|
+
|
|
706
|
+
/*
|
|
707
|
+
* Begin Implement SpatialScrollAble Protocol
|
|
708
|
+
*/
|
|
709
|
+
let scrollPageEnabled: Bool = true
|
|
710
|
+
|
|
711
|
+
var _scrollOffset: Vec2 = .init(x: 0, y: 0)
|
|
712
|
+
var scrollOffset: Vec2 {
|
|
713
|
+
get {
|
|
714
|
+
return _scrollOffset
|
|
715
|
+
}
|
|
716
|
+
set(newValue) {
|
|
717
|
+
_scrollOffset = newValue
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
func updateDeltaScrollOffset(_ delta: Vec2) {
|
|
722
|
+
spatialWebViewModel.setScrollOffset(_scrollOffset + delta)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
func stopScrolling() {
|
|
726
|
+
spatialWebViewModel.stopScrolling()
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/*
|
|
730
|
+
* End Implement SpatialScrollAble Protocol
|
|
731
|
+
*/
|
|
732
|
+
|
|
733
|
+
private var _backgroundMaterial = BackgroundMaterial.None
|
|
734
|
+
var backgroundMaterial: BackgroundMaterial {
|
|
735
|
+
get {
|
|
736
|
+
return _backgroundMaterial
|
|
737
|
+
}
|
|
738
|
+
set(newValue) {
|
|
739
|
+
_backgroundMaterial = newValue
|
|
740
|
+
if windowStyle == .volume {
|
|
741
|
+
// default background for volume scene is transparent
|
|
742
|
+
spatialWebViewModel
|
|
743
|
+
.setBackgroundTransparent(true)
|
|
744
|
+
} else {
|
|
745
|
+
spatialWebViewModel.setBackgroundTransparent(_backgroundMaterial != .None)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
var cornerRadius: CornerRadius = .init()
|
|
751
|
+
|
|
752
|
+
var opacity: Double = 1.0
|
|
753
|
+
|
|
754
|
+
func getView() -> SpatialWebView {
|
|
755
|
+
return spatialWebViewModel.getView()
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/*
|
|
759
|
+
* Begin SpatialObjects management
|
|
760
|
+
*/
|
|
761
|
+
|
|
762
|
+
// Resources that will be destroyed when this webpage is destoryed or if it is navigated away from
|
|
763
|
+
private var spatialObjects = [String: any SpatialObjectProtocol]()
|
|
764
|
+
|
|
765
|
+
func createSpatializedElement<T: SpatializedElement>(_ type: SpatializedElementType) -> T {
|
|
766
|
+
let spatializedElement: T = switch type {
|
|
767
|
+
case .Spatialized2DElement:
|
|
768
|
+
Spatialized2DElement() as! T
|
|
769
|
+
case .SpatializedStatic3DElement:
|
|
770
|
+
SpatializedStatic3DElement() as! T
|
|
771
|
+
case .SpatializedDynamic3DElement:
|
|
772
|
+
SpatializedDynamic3DElement() as! T
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
addSpatialObject(spatializedElement)
|
|
776
|
+
|
|
777
|
+
return spatializedElement
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
private func onCreateGeometry(command: CreateGeometryProperties, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
781
|
+
do {
|
|
782
|
+
let geometry = try Dynamic3DManager.createGeometry(command)
|
|
783
|
+
addSpatialObject(geometry)
|
|
784
|
+
resolve(.success(AddSpatializedElementReply(id: geometry.id)))
|
|
785
|
+
} catch let err as GeometryCreationError {
|
|
786
|
+
switch err {
|
|
787
|
+
case .invalidType:
|
|
788
|
+
resolve(.failure(JsbError(code: .TypeError, message: err.localizedDescription)))
|
|
789
|
+
case .missingFields:
|
|
790
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: err.localizedDescription)))
|
|
791
|
+
}
|
|
792
|
+
} catch {
|
|
793
|
+
resolve(.failure(JsbError(code: .CommandError, message: error.localizedDescription)))
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
private func onCreateEntity(command: CreateSpatialEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
798
|
+
let entity = Dynamic3DManager.createEntity(command)
|
|
799
|
+
addSpatialObject(entity)
|
|
800
|
+
resolve(.success(AddSpatializedElementReply(id: entity.spatialId)))
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
private func onCreateComponent() {
|
|
804
|
+
// @fukang: add Component here
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
private func onCreateUnlitMaterial(command: CreateUnlitMaterial, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
808
|
+
let material = Dynamic3DManager.createUnlitMaterial(command, nil)
|
|
809
|
+
addSpatialObject(material)
|
|
810
|
+
resolve(.success(AddSpatializedElementReply(id: material.id)))
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
private func onCreateModelComponent(command: CreateModelComponent, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
814
|
+
if let geometry = spatialObjects[command.geometryId] as? Geometry {
|
|
815
|
+
var materials: [SpatialMaterial] = []
|
|
816
|
+
for mid in command.materialIds {
|
|
817
|
+
if let material = spatialObjects[mid] as? SpatialMaterial {
|
|
818
|
+
materials.append(material)
|
|
819
|
+
} else {
|
|
820
|
+
print("material \(mid) not found ")
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
let component = Dynamic3DManager.createModelComponent(mesh: geometry, mats: materials)
|
|
824
|
+
addSpatialObject(component)
|
|
825
|
+
resolve(.success(AddSpatializedElementReply(id: component.id)))
|
|
826
|
+
} else {
|
|
827
|
+
print("geometry \(command.geometryId) not found")
|
|
828
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "geometry \(command.geometryId) not found")))
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
private func onAddComponentToEntity(command: AddComponentToEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
833
|
+
if let entity = spatialObjects[command.entityId] as? SpatialEntity,
|
|
834
|
+
let component = spatialObjects[command.componentId] as? SpatialComponent
|
|
835
|
+
{
|
|
836
|
+
entity.addComponent(component)
|
|
837
|
+
resolve(.success(baseReplyData))
|
|
838
|
+
return
|
|
839
|
+
}
|
|
840
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Add component failed")))
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
private func onAddEntityToDynamic3D(command: AddEntityToDynamic3D, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
844
|
+
if let entity = spatialObjects[command.entityId] as? SpatialEntity,
|
|
845
|
+
let dynamic3dElement = spatialObjects[command.dynamic3dId] as? SpatializedDynamic3DElement
|
|
846
|
+
{
|
|
847
|
+
dynamic3dElement.addEntity(entity)
|
|
848
|
+
resolve(.success(baseReplyData))
|
|
849
|
+
return
|
|
850
|
+
}
|
|
851
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Add Entity failed")))
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
private func onAddEntityToEntity(command: AddEntityToEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
855
|
+
if let entityChild = spatialObjects[command.childId] as? SpatialEntity,
|
|
856
|
+
let entityParent = spatialObjects[command.parentId] as? SpatialEntity
|
|
857
|
+
{
|
|
858
|
+
entityParent.addChild(entity: entityChild)
|
|
859
|
+
resolve(.success(baseReplyData))
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Add Entity failed")))
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
private func onSetParentForEntity(command: SetParentForEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
866
|
+
if let entity = spatialObjects[command.childId] as? SpatialEntity {
|
|
867
|
+
if let parentId = command.parentId {
|
|
868
|
+
if let parentEntity = spatialObjects[parentId] as? SpatialEntity {
|
|
869
|
+
parentEntity.addChild(entity: entity)
|
|
870
|
+
} else if let container = spatialObjects[parentId] as? SpatializedDynamic3DElement {
|
|
871
|
+
container.addEntity(entity)
|
|
872
|
+
} else {
|
|
873
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Parent \(parentId) not found")))
|
|
874
|
+
return
|
|
875
|
+
}
|
|
876
|
+
resolve(.success(baseReplyData))
|
|
877
|
+
return
|
|
878
|
+
} else {
|
|
879
|
+
entity.removeFromParent()
|
|
880
|
+
resolve(.success(baseReplyData))
|
|
881
|
+
return
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity \(command.childId) not found")))
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
private func onRemoveEntityFromParent(command: RemoveEntityFromParent, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
888
|
+
if let entity = spatialObjects[command.entityId] as? SpatialEntity {
|
|
889
|
+
if entity.parent != nil,
|
|
890
|
+
let parentEntity = entity.parent as? SpatialEntity
|
|
891
|
+
{
|
|
892
|
+
parentEntity.removeChild(id: entity.spatialId)
|
|
893
|
+
resolve(.success(baseReplyData))
|
|
894
|
+
return
|
|
895
|
+
}
|
|
896
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Parent not found")))
|
|
897
|
+
return
|
|
898
|
+
}
|
|
899
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity not found")))
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
private func onUpdateEntityProperties(command: UpdateEntityProperties, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
903
|
+
if let entity = spatialObjects[command.entityId] as? SpatialEntity,
|
|
904
|
+
command.transform.count == 16
|
|
905
|
+
{
|
|
906
|
+
entity.updateTransform(command.transform)
|
|
907
|
+
resolve(.success(baseReplyData))
|
|
908
|
+
return
|
|
909
|
+
}
|
|
910
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Update Entity failed")))
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
private func onCreateModelAsset(command: CreateModelAsset, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
914
|
+
_ = SpatialModelResource(command.url) { onload in
|
|
915
|
+
switch onload {
|
|
916
|
+
case let .success(modelResource):
|
|
917
|
+
self.addSpatialObject(modelResource)
|
|
918
|
+
resolve(.success(AddSpatializedElementReply(id: modelResource.id)))
|
|
919
|
+
case let .failure(error):
|
|
920
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Failed to download model: \(error)")))
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
private func onCreateSpatialModelEntity(command: CreateSpatialModelEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
926
|
+
if let modelAsset = spatialObjects[command.modelAssetId] as? SpatialModelResource {
|
|
927
|
+
let spatialModelEntity = SpatialModelEntity(modelAsset, command.name ?? "")
|
|
928
|
+
addSpatialObject(spatialModelEntity)
|
|
929
|
+
resolve(.success(AddSpatializedElementReply(id: spatialModelEntity.spatialId)))
|
|
930
|
+
return
|
|
931
|
+
}
|
|
932
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "ModelAsset not found")))
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
private func onUpdateEntityEvent(command: UpdateEntityEvent, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
936
|
+
guard let entity = spatialObjects[command.entityId] as? SpatialEntity else {
|
|
937
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity not found")))
|
|
938
|
+
return
|
|
939
|
+
}
|
|
940
|
+
entity.updateGesture(command.type, command.isEnable)
|
|
941
|
+
resolve(.success(baseReplyData))
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
private func onConvertFromEntityToEntity(command: ConvertFromEntityToEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
945
|
+
guard let fromEntity = spatialObjects[command.fromEntityId] as? SpatialEntity else {
|
|
946
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity \(command.fromEntityId) not found")))
|
|
947
|
+
return
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
guard let toEntity = spatialObjects[command.toEntityId] as? SpatialEntity else {
|
|
951
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity \(command.toEntityId) not found")))
|
|
952
|
+
return
|
|
953
|
+
}
|
|
954
|
+
let position = SIMD3<Float>(Float(command.position.x), Float(command.position.y), Float(command.position.z))
|
|
955
|
+
let point = fromEntity.convert(position: position, to: toEntity)
|
|
956
|
+
resolve(.success(ConvertReply(id: command.fromEntityId, position: point)))
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
private func onConvertFromEntityToScene(command: ConvertFromEntityToScene, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
960
|
+
guard let fromEntity = spatialObjects[command.fromEntityId] as? SpatialEntity else {
|
|
961
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity \(command.fromEntityId) not found")))
|
|
962
|
+
return
|
|
963
|
+
}
|
|
964
|
+
let position = SIMD3<Float>(Float(command.position.x), Float(command.position.y), Float(command.position.z))
|
|
965
|
+
let point = fromEntity.convert(position: position, to: nil)
|
|
966
|
+
resolve(.success(ConvertReply(id: command.fromEntityId, position: point)))
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
private func onConvertFromSceneToEntity(command: ConvertFromSceneToEntity, resolve: @escaping JSBManager.ResolveHandler<Encodable>) {
|
|
970
|
+
guard let entity = spatialObjects[command.entityId] as? SpatialEntity else {
|
|
971
|
+
resolve(.failure(JsbError(code: .InvalidSpatialObject, message: "Entity \(command.entityId) not found")))
|
|
972
|
+
return
|
|
973
|
+
}
|
|
974
|
+
let position = SIMD3<Float>(Float(command.position.x), Float(command.position.y), Float(command.position.z))
|
|
975
|
+
let point = entity.convert(position: position, from: nil)
|
|
976
|
+
resolve(.success(ConvertReply(id: command.entityId, position: point)))
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
private func addSpatialObject(_ object: any SpatialObjectProtocol) {
|
|
980
|
+
var spatialObject = object
|
|
981
|
+
spatialObjects[spatialObject.spatialId] = spatialObject
|
|
982
|
+
spatialObject
|
|
983
|
+
.on(
|
|
984
|
+
event: SpatialObject.Events.BeforeDestroyed.rawValue,
|
|
985
|
+
listener: onSptatialObjectDestroyed
|
|
986
|
+
)
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
private func onSptatialObjectDestroyed(_ object: Any, _ data: Any) {
|
|
990
|
+
var spatialObject = object as! (any SpatialObjectProtocol)
|
|
991
|
+
spatialObject
|
|
992
|
+
.off(
|
|
993
|
+
event: SpatialObject.Events.BeforeDestroyed.rawValue,
|
|
994
|
+
listener: onSptatialObjectDestroyed
|
|
995
|
+
)
|
|
996
|
+
spatialObjects.removeValue(forKey: spatialObject.spatialId)
|
|
997
|
+
|
|
998
|
+
// notify web side, spatialObject is destroyed
|
|
999
|
+
sendWebMsg(spatialObject.spatialId, SpatialObjectDestroiedEvent())
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
func findSpatialObject<T: SpatialObjectProtocol>(_ id: String) -> T? {
|
|
1003
|
+
return spatialObjects[id] as? T
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/*
|
|
1007
|
+
* End SpatialObjects management
|
|
1008
|
+
*/
|
|
1009
|
+
|
|
1010
|
+
override func onDestroy() {
|
|
1011
|
+
let spatialObjectArray = spatialObjects.map { $0.value }
|
|
1012
|
+
for spatialObject in spatialObjectArray {
|
|
1013
|
+
spatialObject.destroy()
|
|
1014
|
+
}
|
|
1015
|
+
spatialWebViewModel.destroy()
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
enum CodingKeys: String, CodingKey {
|
|
1019
|
+
case children, url, backgroundMaterial, cornerRadius, scrollOffset, webviewIsOpaque, spatialObjectCount, spatialObjectRefCount, spatialObjectList
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
override func encode(to encoder: Encoder) throws {
|
|
1023
|
+
try super.encode(to: encoder)
|
|
1024
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
1025
|
+
try container.encode(spatialWebViewModel.url, forKey: .url)
|
|
1026
|
+
try container.encode(backgroundMaterial, forKey: .backgroundMaterial)
|
|
1027
|
+
try container.encode(cornerRadius, forKey: .cornerRadius)
|
|
1028
|
+
try container.encode(scrollOffset, forKey: .scrollOffset)
|
|
1029
|
+
try container.encode(children, forKey: .children)
|
|
1030
|
+
|
|
1031
|
+
// for debug only
|
|
1032
|
+
try container.encode(spatialWebViewModel.getController().webview?.isOpaque, forKey: .webviewIsOpaque)
|
|
1033
|
+
try container.encode(SpatialObject.objects.count, forKey: .spatialObjectCount)
|
|
1034
|
+
|
|
1035
|
+
let spatialObjectList = SpatialObject.objects.map { object in
|
|
1036
|
+
["id": object.key, "type": String(describing: type(of: object.value))]
|
|
1037
|
+
}
|
|
1038
|
+
try container.encode(spatialObjectList, forKey: .spatialObjectList)
|
|
1039
|
+
|
|
1040
|
+
try container.encode(SpatialObjectWeakRefManager.weakRefObjects.count, forKey: .spatialObjectRefCount)
|
|
1041
|
+
}
|
|
1042
|
+
}
|