Skip to content

Commit c0cd451

Browse files
committed
Add loadSync and enable generateCollisionShapes for all entity types
1 parent 846600c commit c0cd451

12 files changed

+115
-55
lines changed

Sources/LiveViewNativeRealityKit/Components/AccessibilityComponent.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Foundation
1111
import RealityKit
1212

1313
extension AccessibilityComponent {
14-
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
14+
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
1515
self.init()
1616

1717
self.label = element.attributeValue(for: "label").flatMap(LocalizedStringResource.init(stringLiteral:))

Sources/LiveViewNativeRealityKit/Components/AnchoringComponent.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import LiveViewNativeCore
1010
import RealityKit
1111

1212
extension AnchoringComponent {
13-
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
13+
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
1414
let target = try element.attributeValue(AnchoringComponent.Target.self, for: "target")
1515
if let trackingMode = try? element.attributeValue(AnchoringComponent.TrackingMode.self, for: "trackingMode") {
1616
self.init(target, trackingMode: trackingMode)

Sources/LiveViewNativeRealityKit/Components/CollisionComponent.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import LiveViewNativeCore
1111
import RealityKit
1212

1313
extension CollisionComponent {
14-
init<C: ComponentRegistry>(from element: ElementNode, in context: ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
15-
let shapes = try ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context)
14+
init<C: ComponentRegistry>(from element: ElementNode, in context: _ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
15+
let shapes = try _ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context)
1616
let isStatic = element.attributeBoolean(for: "isStatic")
1717
let filter = try? element.attributeValue(CollisionFilter.self, for: "filter")
1818
let mode = try? element.attributeValue(Mode.self, for: "mode")

Sources/LiveViewNativeRealityKit/Components/GroundingShadowComponent.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import LiveViewNative
99
import RealityKit
1010

1111
extension GroundingShadowComponent {
12-
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
12+
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
1313
self.init(
1414
castsShadow: element.attributeBoolean(for: "castsShadow")
1515
)

Sources/LiveViewNativeRealityKit/Components/HoverEffectComponent.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import RealityKit
1010
import SwiftUI
1111

1212
extension HoverEffectComponent {
13-
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
13+
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
1414
if #available(visionOS 2, *) {
1515
switch element.attributeValue(for: "hoverEffect") {
1616
case "spotlight":

Sources/LiveViewNativeRealityKit/Components/OpacityComponent.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import LiveViewNative
99
import RealityKit
1010

1111
extension OpacityComponent {
12-
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
12+
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
1313
self.init(
1414
opacity: (try? element.attributeValue(Float.self, for: "opacity")) ?? 1
1515
)

Sources/LiveViewNativeRealityKit/Components/PhysicsBodyComponent.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import LiveViewNativeCore
1010
import RealityKit
1111

1212
extension PhysicsBodyComponent {
13-
init<C: ComponentRegistry>(from element: ElementNode, in context: ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
13+
init<C: ComponentRegistry>(from element: ElementNode, in context: _ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
1414
if let mass = try? element.attributeValue(Float.self, for: "mass") {
1515
self.init(
16-
shapes: try ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context),
16+
shapes: try _ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context),
1717
mass: mass,
1818
material: try? PhysicsMaterialResource.generate(from: element.attribute(named: "material"), on: element),
1919
mode: (try? element.attributeValue(PhysicsBodyMode.self, for: "mode")) ?? .dynamic

Sources/LiveViewNativeRealityKit/ContentBuilders/ComponentContentBuilder.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import OSLog
1212

1313
private let logger = Logger(subsystem: "LiveViewNativeRealityKit", category: "ComponentContentBuilder")
1414

15-
struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry {
16-
enum TagName: RawRepresentable {
15+
public struct _ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry {
16+
public enum TagName: RawRepresentable {
1717
case builtin(Builtin)
1818
case custom(Components.TagName)
1919

20-
enum Builtin: String {
20+
public enum Builtin: String {
2121
case group = "Group"
2222

2323
case anchoringComponent = "AnchoringComponent"
@@ -30,9 +30,9 @@ struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry
3030
case hoverEffectComponent = "HoverEffectComponent"
3131
}
3232

33-
typealias RawValue = String
33+
public typealias RawValue = String
3434

35-
init?(rawValue: String) {
35+
public init?(rawValue: String) {
3636
if let builtin = Builtin.init(rawValue: rawValue) {
3737
self = .builtin(builtin)
3838
} else if let custom = Components.TagName.init(rawValue: rawValue) {
@@ -42,7 +42,7 @@ struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry
4242
}
4343
}
4444

45-
var rawValue: RawValue {
45+
public var rawValue: RawValue {
4646
switch self {
4747
case .builtin(let builtin):
4848
builtin.rawValue
@@ -52,7 +52,7 @@ struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry
5252
}
5353
}
5454

55-
static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> [any Component] {
55+
public static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> [any Component] {
5656
do {
5757
switch tag {
5858
case let .builtin(builtin):

Sources/LiveViewNativeRealityKit/ContentBuilders/EntityContentBuilder.swift

+15-8
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ import OSLog
1313

1414
private let logger = Logger(subsystem: "LiveViewNativeRealityKit", category: "EntityContentBuilder")
1515

16-
struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegistry>: EntityRegistry {
17-
enum TagName: RawRepresentable {
16+
public struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegistry>: EntityRegistry {
17+
public enum TagName: RawRepresentable {
1818
case builtin(Builtin)
1919
case custom(Entities.TagName)
2020

21-
typealias RawValue = String
21+
public typealias RawValue = String
2222

23-
enum Builtin: String {
23+
public enum Builtin: String {
2424
case group = "Group"
2525
case entity = "Entity"
2626
case modelEntity = "ModelEntity"
@@ -32,7 +32,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
3232
case viewAttachmentEntity = "ViewAttachmentEntity"
3333
}
3434

35-
init?(rawValue: RawValue) {
35+
public init?(rawValue: RawValue) {
3636
if let builtin = Builtin(rawValue: rawValue) {
3737
self = .builtin(builtin)
3838
} else if let custom = Entities.TagName.init(rawValue: rawValue) {
@@ -42,7 +42,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
4242
}
4343
}
4444

45-
var rawValue: RawValue {
45+
public var rawValue: RawValue {
4646
switch self {
4747
case .builtin(let builtin):
4848
builtin.rawValue
@@ -52,7 +52,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
5252
}
5353
}
5454

55-
static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> Content {
55+
public static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> Content {
5656
let entity: Entity
5757
do {
5858
switch tag {
@@ -63,7 +63,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
6363
return children
6464
case .entity:
6565
if element.attribute(named: "url") != nil || element.attribute(named: "named") != nil {
66-
entity = AsyncEntity(from: element, in: context)
66+
entity = try AsyncEntity(from: element, in: context)
6767
} else {
6868
entity = Entity()
6969
}
@@ -179,6 +179,13 @@ extension Entity {
179179
if let asyncEntity = self as? AsyncEntity {
180180
try asyncEntity.updateResolvedEntity(with: element, in: context)
181181
}
182+
183+
if element.attributeBoolean(for: "generateCollisionShapes") {
184+
self.generateCollisionShapes(
185+
recursive: element.attributeBoolean(for: .init(namespace: "generateCollisionShapes", name: "recursive")),
186+
static: element.attributeBoolean(for: .init(namespace: "generateCollisionShapes", name: "static"))
187+
)
188+
}
182189
}
183190

184191
func applyChildren<R: RootRegistry, E: EntityRegistry, C: ComponentRegistry>(

Sources/LiveViewNativeRealityKit/Entities/Custom/AsyncEntity.swift

+72-14
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,91 @@ import LiveViewNative
1212
final class AsyncEntity: Entity {
1313
var setupTask: Task<(), Error>? = nil
1414

15+
static var entityCache = [URL:Entity]()
16+
1517
init<E: EntityRegistry, C: ComponentRegistry>(
1618
from element: ElementNode,
1719
in context: EntityContentBuilder<E, C>.Context<some RootRegistry>
18-
) {
20+
) throws {
1921
AsyncEntityComponent.registerComponent()
2022

2123
super.init()
2224

25+
let loadSync = element.attributeBoolean(for: "loadSync")
26+
2327
if let url = element.attributeValue(for: "url").flatMap({ URL(string: $0, relativeTo: context.url) }) {
24-
setupTask = Task { [weak self] in
25-
let (fileURL, _) = try await URLSession.shared.download(from: url)
26-
let correctedExtensionURL = fileURL.deletingPathExtension().appendingPathExtension(for: .usdz)
27-
try FileManager.default.moveItem(at: fileURL, to: correctedExtensionURL)
28+
if let cached = Self.entityCache[url] {
29+
let entity = cached.clone(recursive: true)
30+
entity.components.set(AsyncEntityComponent.remote(url))
31+
self.addChild(entity)
32+
} else if loadSync {
33+
let group = DispatchGroup()
34+
group.enter()
2835

29-
do {
30-
let entity = try await Entity(contentsOf: correctedExtensionURL)
31-
entity.components.set(AsyncEntityComponent.remote(url))
32-
self?.addChild(entity)
33-
34-
try self?.updateResolvedEntity(with: element, in: context)
36+
var fileURL: Result<URL, Error>!
37+
38+
let downloadTask = URLSession.shared.downloadTask(with: url) { result, _, error in
39+
if let result {
40+
fileURL = .success(result)
41+
} else {
42+
fileURL = .failure(error!)
43+
}
44+
group.leave()
3545
}
46+
downloadTask.resume()
3647

37-
try FileManager.default.removeItem(at: correctedExtensionURL)
48+
group.wait()
49+
50+
switch fileURL! {
51+
case .success(let fileURL):
52+
let correctedExtensionURL = fileURL.deletingPathExtension().appendingPathExtension(for: .usdz)
53+
try FileManager.default.moveItem(at: fileURL, to: correctedExtensionURL)
54+
55+
do {
56+
let entity = try Entity.load(contentsOf: correctedExtensionURL)
57+
Self.entityCache[url] = entity.clone(recursive: true)
58+
entity.components.set(AsyncEntityComponent.remote(url))
59+
self.addChild(entity)
60+
61+
try self.updateResolvedEntity(with: element, in: context)
62+
} catch {
63+
try FileManager.default.removeItem(at: correctedExtensionURL)
64+
throw error
65+
}
66+
67+
try FileManager.default.removeItem(at: correctedExtensionURL)
68+
case .failure(let error):
69+
throw error
70+
}
71+
} else {
72+
setupTask = Task { [weak self] in
73+
let (fileURL, _) = try await URLSession.shared.download(from: url)
74+
let correctedExtensionURL = fileURL.deletingPathExtension().appendingPathExtension(for: .usdz)
75+
try FileManager.default.moveItem(at: fileURL, to: correctedExtensionURL)
76+
77+
do {
78+
let entity = try await Entity(contentsOf: correctedExtensionURL)
79+
Self.entityCache[url] = entity.clone(recursive: true)
80+
entity.components.set(AsyncEntityComponent.remote(url))
81+
self?.addChild(entity)
82+
83+
try self?.updateResolvedEntity(with: element, in: context)
84+
} catch {
85+
try FileManager.default.removeItem(at: correctedExtensionURL)
86+
throw error
87+
}
88+
try FileManager.default.removeItem(at: correctedExtensionURL)
89+
}
3890
}
3991
} else if let named = element.attributeValue(for: "named") {
40-
setupTask = Task { [weak self] in
41-
do {
92+
if loadSync {
93+
let entity = try Entity.load(named: named)
94+
entity.components.set(AsyncEntityComponent.named(named))
95+
self.addChild(entity)
96+
97+
try self.updateResolvedEntity(with: element, in: context)
98+
} else {
99+
setupTask = Task { [weak self] in
42100
let entity = try await Entity(named: named)
43101
entity.components.set(AsyncEntityComponent.named(named))
44102
self?.addChild(entity)

Sources/LiveViewNativeRealityKit/RealityKit.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public struct CustomizableRealityKitRegistry<
1414
public static func lookup(_ name: TagName, element: ElementNode) -> some View {
1515
switch name {
1616
case .realityView:
17-
_RealityView<Root, Entities, ComponentContentBuilder<Components>>()
17+
_RealityView<Root, Entities, _ComponentContentBuilder<Components>>()
1818
}
1919
}
2020
}

Sources/LiveViewNativeRealityKit/RealityView.swift

+11-16
Original file line numberDiff line numberDiff line change
@@ -146,22 +146,6 @@ struct _RealityView<Root: RootRegistry, Entities: EntityRegistry, Components: Co
146146

147147
var body: some View {
148148
RealityView { content, attachments in
149-
let updateContext = Entity()
150-
updateContext.components.set(UpdateContextComponent<Root, Entities, Components>(
151-
storage: self.updateStorage,
152-
document: context.document,
153-
context: context,
154-
attachments: attachments
155-
))
156-
content.add(updateContext)
157-
do {
158-
for entity in try EntityContentBuilder<Entities, Components>.buildChildren(of: element, in: context) {
159-
content.add(entity)
160-
}
161-
} catch {
162-
logger.error("Entities failed to build with: \(error)")
163-
}
164-
165149
self.subscriptions = [
166150
content.subscribe(to: CollisionEvents.Began.self, componentType: PhysicsBodyChangeEventComponent.self) { collision in
167151
for entity in [collision.entityA, collision.entityB] {
@@ -208,6 +192,17 @@ struct _RealityView<Root: RootRegistry, Entities: EntityRegistry, Components: Co
208192
}
209193
}
210194
]
195+
196+
let updateContext = Entity()
197+
updateContext.components.set(UpdateContextComponent<Root, Entities, Components>(storage: self.updateStorage, document: context.document, context: context, attachments: attachments))
198+
content.add(updateContext)
199+
do {
200+
for entity in try EntityContentBuilder<Entities, Components>.buildChildren(of: element, in: context) {
201+
content.add(entity)
202+
}
203+
} catch {
204+
logger.error("Entities failed to build with: \(error)")
205+
}
211206
} update: { content, attachments in
212207
if self.updateStorage.updates.contains(self.$liveElement.element.id) {
213208
guard let element: ElementNode = self.context.document?[self.$liveElement.element.id].asElement()

0 commit comments

Comments
 (0)