Skip to content

Commit

Permalink
Add loadSync and enable generateCollisionShapes for all entity types
Browse files Browse the repository at this point in the history
  • Loading branch information
carson-katri committed Jul 25, 2024
1 parent 846600c commit c0cd451
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
import RealityKit

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

self.label = element.attributeValue(for: "label").flatMap(LocalizedStringResource.init(stringLiteral:))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import LiveViewNativeCore
import RealityKit

extension AnchoringComponent {
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
let target = try element.attributeValue(AnchoringComponent.Target.self, for: "target")
if let trackingMode = try? element.attributeValue(AnchoringComponent.TrackingMode.self, for: "trackingMode") {
self.init(target, trackingMode: trackingMode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import LiveViewNativeCore
import RealityKit

extension CollisionComponent {
init<C: ComponentRegistry>(from element: ElementNode, in context: ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
let shapes = try ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context)
init<C: ComponentRegistry>(from element: ElementNode, in context: _ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
let shapes = try _ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context)
let isStatic = element.attributeBoolean(for: "isStatic")
let filter = try? element.attributeValue(CollisionFilter.self, for: "filter")
let mode = try? element.attributeValue(Mode.self, for: "mode")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import LiveViewNative
import RealityKit

extension GroundingShadowComponent {
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
self.init(
castsShadow: element.attributeBoolean(for: "castsShadow")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import RealityKit
import SwiftUI

extension HoverEffectComponent {
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
if #available(visionOS 2, *) {
switch element.attributeValue(for: "hoverEffect") {
case "spotlight":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import LiveViewNative
import RealityKit

extension OpacityComponent {
init(from element: ElementNode, in context: ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
init(from element: ElementNode, in context: _ComponentContentBuilder<some ComponentRegistry>.Context<some RootRegistry>) throws {
self.init(
opacity: (try? element.attributeValue(Float.self, for: "opacity")) ?? 1
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import LiveViewNativeCore
import RealityKit

extension PhysicsBodyComponent {
init<C: ComponentRegistry>(from element: ElementNode, in context: ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
init<C: ComponentRegistry>(from element: ElementNode, in context: _ComponentContentBuilder<C>.Context<some RootRegistry>) throws {
if let mass = try? element.attributeValue(Float.self, for: "mass") {
self.init(
shapes: try ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context),
shapes: try _ComponentContentBuilder<C>.buildChildren(of: element, with: ShapeResourceContentBuilder.self, in: context),
mass: mass,
material: try? PhysicsMaterialResource.generate(from: element.attribute(named: "material"), on: element),
mode: (try? element.attributeValue(PhysicsBodyMode.self, for: "mode")) ?? .dynamic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import OSLog

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

struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry {
enum TagName: RawRepresentable {
public struct _ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry {
public enum TagName: RawRepresentable {
case builtin(Builtin)
case custom(Components.TagName)

enum Builtin: String {
public enum Builtin: String {
case group = "Group"

case anchoringComponent = "AnchoringComponent"
Expand All @@ -30,9 +30,9 @@ struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry
case hoverEffectComponent = "HoverEffectComponent"
}

typealias RawValue = String
public typealias RawValue = String

init?(rawValue: String) {
public init?(rawValue: String) {
if let builtin = Builtin.init(rawValue: rawValue) {
self = .builtin(builtin)
} else if let custom = Components.TagName.init(rawValue: rawValue) {
Expand All @@ -42,7 +42,7 @@ struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry
}
}

var rawValue: RawValue {
public var rawValue: RawValue {
switch self {
case .builtin(let builtin):
builtin.rawValue
Expand All @@ -52,7 +52,7 @@ struct ComponentContentBuilder<Components: ComponentRegistry>: ComponentRegistry
}
}

static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> [any Component] {
public static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> [any Component] {
do {
switch tag {
case let .builtin(builtin):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import OSLog

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

struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegistry>: EntityRegistry {
enum TagName: RawRepresentable {
public struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegistry>: EntityRegistry {
public enum TagName: RawRepresentable {
case builtin(Builtin)
case custom(Entities.TagName)

typealias RawValue = String
public typealias RawValue = String

enum Builtin: String {
public enum Builtin: String {
case group = "Group"
case entity = "Entity"
case modelEntity = "ModelEntity"
Expand All @@ -32,7 +32,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
case viewAttachmentEntity = "ViewAttachmentEntity"
}

init?(rawValue: RawValue) {
public init?(rawValue: RawValue) {
if let builtin = Builtin(rawValue: rawValue) {
self = .builtin(builtin)
} else if let custom = Entities.TagName.init(rawValue: rawValue) {
Expand All @@ -42,7 +42,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
}
}

var rawValue: RawValue {
public var rawValue: RawValue {
switch self {
case .builtin(let builtin):
builtin.rawValue
Expand All @@ -52,7 +52,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
}
}

static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> Content {
public static func lookup<R: RootRegistry>(_ tag: TagName, element: LiveViewNative.ElementNode, context: Context<R>) -> Content {
let entity: Entity
do {
switch tag {
Expand All @@ -63,7 +63,7 @@ struct EntityContentBuilder<Entities: EntityRegistry, Components: ComponentRegis
return children
case .entity:
if element.attribute(named: "url") != nil || element.attribute(named: "named") != nil {
entity = AsyncEntity(from: element, in: context)
entity = try AsyncEntity(from: element, in: context)
} else {
entity = Entity()
}
Expand Down Expand Up @@ -179,6 +179,13 @@ extension Entity {
if let asyncEntity = self as? AsyncEntity {
try asyncEntity.updateResolvedEntity(with: element, in: context)
}

if element.attributeBoolean(for: "generateCollisionShapes") {
self.generateCollisionShapes(
recursive: element.attributeBoolean(for: .init(namespace: "generateCollisionShapes", name: "recursive")),
static: element.attributeBoolean(for: .init(namespace: "generateCollisionShapes", name: "static"))
)
}
}

func applyChildren<R: RootRegistry, E: EntityRegistry, C: ComponentRegistry>(
Expand Down
86 changes: 72 additions & 14 deletions Sources/LiveViewNativeRealityKit/Entities/Custom/AsyncEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,91 @@ import LiveViewNative
final class AsyncEntity: Entity {
var setupTask: Task<(), Error>? = nil

static var entityCache = [URL:Entity]()

init<E: EntityRegistry, C: ComponentRegistry>(
from element: ElementNode,
in context: EntityContentBuilder<E, C>.Context<some RootRegistry>
) {
) throws {
AsyncEntityComponent.registerComponent()

super.init()

let loadSync = element.attributeBoolean(for: "loadSync")

if let url = element.attributeValue(for: "url").flatMap({ URL(string: $0, relativeTo: context.url) }) {
setupTask = Task { [weak self] in
let (fileURL, _) = try await URLSession.shared.download(from: url)
let correctedExtensionURL = fileURL.deletingPathExtension().appendingPathExtension(for: .usdz)
try FileManager.default.moveItem(at: fileURL, to: correctedExtensionURL)
if let cached = Self.entityCache[url] {
let entity = cached.clone(recursive: true)
entity.components.set(AsyncEntityComponent.remote(url))
self.addChild(entity)
} else if loadSync {
let group = DispatchGroup()
group.enter()

do {
let entity = try await Entity(contentsOf: correctedExtensionURL)
entity.components.set(AsyncEntityComponent.remote(url))
self?.addChild(entity)

try self?.updateResolvedEntity(with: element, in: context)
var fileURL: Result<URL, Error>!

let downloadTask = URLSession.shared.downloadTask(with: url) { result, _, error in
if let result {
fileURL = .success(result)
} else {
fileURL = .failure(error!)
}
group.leave()
}
downloadTask.resume()

try FileManager.default.removeItem(at: correctedExtensionURL)
group.wait()

switch fileURL! {
case .success(let fileURL):
let correctedExtensionURL = fileURL.deletingPathExtension().appendingPathExtension(for: .usdz)
try FileManager.default.moveItem(at: fileURL, to: correctedExtensionURL)

do {
let entity = try Entity.load(contentsOf: correctedExtensionURL)
Self.entityCache[url] = entity.clone(recursive: true)
entity.components.set(AsyncEntityComponent.remote(url))
self.addChild(entity)

try self.updateResolvedEntity(with: element, in: context)
} catch {
try FileManager.default.removeItem(at: correctedExtensionURL)
throw error
}

try FileManager.default.removeItem(at: correctedExtensionURL)
case .failure(let error):
throw error
}
} else {
setupTask = Task { [weak self] in
let (fileURL, _) = try await URLSession.shared.download(from: url)
let correctedExtensionURL = fileURL.deletingPathExtension().appendingPathExtension(for: .usdz)
try FileManager.default.moveItem(at: fileURL, to: correctedExtensionURL)

do {
let entity = try await Entity(contentsOf: correctedExtensionURL)
Self.entityCache[url] = entity.clone(recursive: true)
entity.components.set(AsyncEntityComponent.remote(url))
self?.addChild(entity)

try self?.updateResolvedEntity(with: element, in: context)
} catch {
try FileManager.default.removeItem(at: correctedExtensionURL)
throw error
}
try FileManager.default.removeItem(at: correctedExtensionURL)
}
}
} else if let named = element.attributeValue(for: "named") {
setupTask = Task { [weak self] in
do {
if loadSync {
let entity = try Entity.load(named: named)
entity.components.set(AsyncEntityComponent.named(named))
self.addChild(entity)

try self.updateResolvedEntity(with: element, in: context)
} else {
setupTask = Task { [weak self] in
let entity = try await Entity(named: named)
entity.components.set(AsyncEntityComponent.named(named))
self?.addChild(entity)
Expand Down
2 changes: 1 addition & 1 deletion Sources/LiveViewNativeRealityKit/RealityKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct CustomizableRealityKitRegistry<
public static func lookup(_ name: TagName, element: ElementNode) -> some View {
switch name {
case .realityView:
_RealityView<Root, Entities, ComponentContentBuilder<Components>>()
_RealityView<Root, Entities, _ComponentContentBuilder<Components>>()
}
}
}
Expand Down
27 changes: 11 additions & 16 deletions Sources/LiveViewNativeRealityKit/RealityView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,22 +146,6 @@ struct _RealityView<Root: RootRegistry, Entities: EntityRegistry, Components: Co

var body: some View {
RealityView { content, attachments in
let updateContext = Entity()
updateContext.components.set(UpdateContextComponent<Root, Entities, Components>(
storage: self.updateStorage,
document: context.document,
context: context,
attachments: attachments
))
content.add(updateContext)
do {
for entity in try EntityContentBuilder<Entities, Components>.buildChildren(of: element, in: context) {
content.add(entity)
}
} catch {
logger.error("Entities failed to build with: \(error)")
}

self.subscriptions = [
content.subscribe(to: CollisionEvents.Began.self, componentType: PhysicsBodyChangeEventComponent.self) { collision in
for entity in [collision.entityA, collision.entityB] {
Expand Down Expand Up @@ -208,6 +192,17 @@ struct _RealityView<Root: RootRegistry, Entities: EntityRegistry, Components: Co
}
}
]

let updateContext = Entity()
updateContext.components.set(UpdateContextComponent<Root, Entities, Components>(storage: self.updateStorage, document: context.document, context: context, attachments: attachments))
content.add(updateContext)
do {
for entity in try EntityContentBuilder<Entities, Components>.buildChildren(of: element, in: context) {
content.add(entity)
}
} catch {
logger.error("Entities failed to build with: \(error)")
}
} update: { content, attachments in
if self.updateStorage.updates.contains(self.$liveElement.element.id) {
guard let element: ElementNode = self.context.document?[self.$liveElement.element.id].asElement()
Expand Down

0 comments on commit c0cd451

Please sign in to comment.