From c3b8b0ec4740b0c40e01c7dfd71a47164deffa40 Mon Sep 17 00:00:00 2001 From: Javier Segura Date: Tue, 4 Aug 2026 07:48:37 +0200 Subject: [PATCH 1/8] [Feature] Drive external physics backends via an EngineExtension coordinator PR B of the physics plugin readiness plan (discussion #1116), built on the EngineExtension infrastructure from #1127 instead of a custom runFrame seam: - PhysicsCoordinator (EngineExtension) steps the installed backend once per fixed substep: body-set diffing against the RigidBodyComponent + ColliderComponent query, batch kinematic writes, step, batch transform read-back into LocalTransformComponent, event drain into an inert sink (subscriptions arrive in the events PR). - PhysicsBackendRegistry schedules/unschedules the coordinator in EngineExtensionRegistry on install/uninstall and locks for runtime on the first simulated substep. - Gravity for the built-in integrator now reads from the global PhysicsWorldConfiguration (default unchanged at (0, -9.8, 0)), so legacy and backend-owned bodies share one gravity. With no backend installed nothing is scheduled and behavior is unchanged; the built-in integrator keeps running legacy entities either way. --- .../Physics/PhysicsBackendRegistry.swift | 21 +- .../Physics/PhysicsCoordinator.swift | 262 ++++++++++++ .../UntoldEngine/Systems/PhysicsSystem.swift | 3 +- .../PhysicsCoordinatorTests.swift | 377 ++++++++++++++++++ 4 files changed, 658 insertions(+), 5 deletions(-) create mode 100644 Sources/UntoldEngine/Physics/PhysicsCoordinator.swift create mode 100644 Tests/UntoldEngineTests/PhysicsCoordinatorTests.swift diff --git a/Sources/UntoldEngine/Physics/PhysicsBackendRegistry.swift b/Sources/UntoldEngine/Physics/PhysicsBackendRegistry.swift index d9a051abe..ea7dcf147 100644 --- a/Sources/UntoldEngine/Physics/PhysicsBackendRegistry.swift +++ b/Sources/UntoldEngine/Physics/PhysicsBackendRegistry.swift @@ -214,8 +214,9 @@ private struct InstalledPhysicsBackendPlugin { /// active is rejected — uninstall first. When no plugin is installed the engine /// uses its built-in integrator, exactly as before this registry existed. /// -/// Install before `UntoldRenderer.create(...)`. Once the engine locks the registry -/// for runtime, install and uninstall are rejected. +/// Install before `UntoldRenderer.create(...)`. The registry locks for runtime on +/// the first fixed substep the coordinator simulates with the installed backend; +/// from then on install and uninstall are rejected. public final class PhysicsBackendRegistry: @unchecked Sendable { public static let shared = PhysicsBackendRegistry() @@ -244,22 +245,28 @@ public final class PhysicsBackendRegistry: @unchecked Sendable { } lock.lock() - defer { lock.unlock() } guard !lockedForRuntime else { let failure = PhysicsBackendPluginFailure(registryLocked: true) failuresByPluginID[manifest.id] = failure + lock.unlock() return .rejected(failure) } if let active = installedPlugin, active.manifest.id != manifest.id { let failure = PhysicsBackendPluginFailure(conflictingPluginID: active.manifest.id) failuresByPluginID[manifest.id] = failure + lock.unlock() return .rejected(failure) } let replacing = installedPlugin != nil installedPlugin = InstalledPhysicsBackendPlugin(manifest: manifest, backend: backend) failuresByPluginID.removeValue(forKey: manifest.id) + lock.unlock() + + // Outside the lock: configures the backend and schedules the coordinator + // in EngineExtensionRegistry (idempotent for a replace of the same ID). + PhysicsCoordinator.shared.backendDidInstall(backend) return replacing ? .replaced : .installed } @@ -267,12 +274,15 @@ public final class PhysicsBackendRegistry: @unchecked Sendable { @discardableResult public func uninstall(id: String) -> Bool { lock.lock() - defer { lock.unlock() } guard !lockedForRuntime, installedPlugin?.manifest.id == id else { + lock.unlock() return false } installedPlugin = nil failuresByPluginID.removeValue(forKey: id) + lock.unlock() + + PhysicsCoordinator.shared.backendDidUninstall() return true } @@ -314,5 +324,8 @@ public final class PhysicsBackendRegistry: @unchecked Sendable { failuresByPluginID.removeAll() lockedForRuntime = false lock.unlock() + + PhysicsCoordinator.shared.backendDidUninstall() + PhysicsCoordinator.shared.resetForTesting() } } diff --git a/Sources/UntoldEngine/Physics/PhysicsCoordinator.swift b/Sources/UntoldEngine/Physics/PhysicsCoordinator.swift new file mode 100644 index 000000000..686ecd1d8 --- /dev/null +++ b/Sources/UntoldEngine/Physics/PhysicsCoordinator.swift @@ -0,0 +1,262 @@ +// +// PhysicsCoordinator.swift +// UntoldEngine +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import Foundation +import simd + +/// Counts overflow reports and discards everything else. Event delivery to +/// subscribers (and USC `OnCollision`) is wired in the physics-events PR; +/// until then a backend's events are drained but go nowhere. +final class InertPhysicsEventSink: PhysicsEventSink { + private(set) var droppedEventCount = 0 + + func receiveContact(_: PhysicsContactEvent) {} + func receiveTrigger(_: PhysicsTriggerEvent) {} + func receiveActivation(_: PhysicsBodyActivationEvent) {} + func reportDroppedEvents(count: Int) { + droppedEventCount += count + } + + func reset() { + droppedEventCount = 0 + } +} + +/// Drives the installed external physics backend from the engine's +/// fixed-timestep loop as an `EngineExtension` — no engine-core seams. +/// +/// `PhysicsBackendRegistry` registers the coordinator with +/// `EngineExtensionRegistry` when a backend plugin installs and unregisters it +/// on uninstall, so with no backend installed nothing is scheduled and the +/// engine behaves exactly as before. The built-in integrator is untouched +/// either way: it keeps running for legacy `PhysicsComponents`/ +/// `KineticComponent` entities, while the external backend owns only entities +/// carrying both `RigidBodyComponent` and `ColliderComponent`. +/// +/// Each `fixedUpdate` (frame thread, once per fixed substep, right after +/// `updatePhysicsSystem`): +/// 1. locks `PhysicsBackendRegistry` for runtime on the first simulated substep, +/// freezing the backend choice for the rest of the run; +/// 2. diffs the current `RigidBodyComponent`+`ColliderComponent` query result +/// against the backend's known-body set → `didAddBody`/`didRemoveBody` +/// (destruction needs no extra hook: component cleanup removes the entity +/// from the query and the diff picks it up); +/// 3. batch-writes kinematic body targets; +/// 4. steps the backend; +/// 5. batch-reads active transforms back into `LocalTransformComponent` for +/// dynamic bodies, marking them dirty for the scenegraph/octree exactly like +/// the built-in integrator does; +/// 6. drains buffered events. +public final class PhysicsCoordinator: EngineExtension, @unchecked Sendable { + public static let shared = PhysicsCoordinator() + + public let id = "com.untoldengine.physics.coordinator" + + private let configLock = NSLock() + private var worldConfig = PhysicsWorldConfiguration() + + // Frame-thread state: touched only from fixedUpdate and the install path, + // which the registry contract requires to happen before runtime starts. + private var knownBodies: Set = [] + private var readbackEntities: [EntityID] = [] + private var readbackTransforms: [PhysicsBodyTransform] = [] + private var kinematicEntities: [EntityID] = [] + private var kinematicTransforms: [PhysicsBodyTransform] = [] + let eventSink = InertPhysicsEventSink() + + private init() {} + + // MARK: - World configuration + + /// Applies world settings to the active backend immediately and to any + /// backend installed later. The built-in integrator reads `gravity` from + /// here as well, so legacy and backend-owned bodies share one gravity. + public func setWorldConfiguration(_ config: PhysicsWorldConfiguration) { + configLock.lock() + worldConfig = config + configLock.unlock() + PhysicsBackendRegistry.shared.activeBackend()?.configure(config) + } + + public func worldConfiguration() -> PhysicsWorldConfiguration { + configLock.lock() + defer { configLock.unlock() } + return worldConfig + } + + var worldGravity: simd_float3 { + configLock.lock() + defer { configLock.unlock() } + return worldConfig.gravity + } + + // MARK: - Install hooks (called by PhysicsBackendRegistry, post-transaction) + + func backendDidInstall(_ backend: any PhysicsBackend) { + backend.configure(worldConfiguration()) + knownBodies.removeAll() + eventSink.reset() + EngineExtensionRegistry.shared.register(self) + } + + func backendDidUninstall() { + EngineExtensionRegistry.shared.unregister(id: id) + } + + // MARK: - EngineExtension + + public func fixedUpdate(deltaTime: Float, context _: EngineExtensionUpdateContext) { + guard let backend = PhysicsBackendRegistry.shared.activeBackend() else { return } + + if !PhysicsBackendRegistry.shared.isLockedForRuntime { + PhysicsBackendRegistry.shared.lockForRuntime() + } + + syncBodySet(with: backend) + pushKinematicTargets(to: backend) + backend.step(deltaTime: deltaTime) + pullTransforms(from: backend) + backend.drainEvents(into: eventSink) + } + + public func willUnregister() { + knownBodies.removeAll() + } + + /// Restores defaults. Test support only. + func resetForTesting() { + configLock.lock() + worldConfig = PhysicsWorldConfiguration() + configLock.unlock() + knownBodies.removeAll() + eventSink.reset() + } + + // MARK: - Body lifecycle + + private func syncBodySet(with backend: any PhysicsBackend) { + let bodyEntities = queryEntities(with: [ + RigidBodyComponent.self, ColliderComponent.self, LocalTransformComponent.self, + ]) + let currentSet = Set(bodyEntities) + + for entity in knownBodies.subtracting(currentSet) { + backend.didRemoveBody(entity: entity) + knownBodies.remove(entity) + } + + for entity in bodyEntities where !knownBodies.contains(entity) { + guard let descriptor = makeBodyDescriptor(entityId: entity) else { continue } + backend.didAddBody(entity: entity, descriptor: descriptor) + knownBodies.insert(entity) + } + } + + private func makeBodyDescriptor(entityId: EntityID) -> PhysicsBodyDescriptor? { + guard let rigidBody = scene.get(component: RigidBodyComponent.self, for: entityId), + let collider = scene.get(component: ColliderComponent.self, for: entityId), + let transform = scene.get(component: LocalTransformComponent.self, for: entityId) + else { + return nil + } + + return PhysicsBodyDescriptor( + motionType: rigidBody.motionType, + collider: PhysicsColliderDescriptor( + shape: collider.shape, + localOffset: collider.localOffset, + friction: collider.friction, + restitution: collider.restitution, + isTrigger: collider.isTrigger + ), + mass: rigidBody.mass, + layer: rigidBody.layer, + collisionMask: rigidBody.collisionMask, + gravityScale: rigidBody.gravityScale, + position: transform.position, + orientation: transform.rotation, + linearVelocity: rigidBody.initialLinearVelocity, + angularVelocity: rigidBody.initialAngularVelocity + ) + } + + // MARK: - Transform transfer + + private func pushKinematicTargets(to backend: any PhysicsBackend) { + kinematicEntities.removeAll(keepingCapacity: true) + kinematicTransforms.removeAll(keepingCapacity: true) + + for entity in knownBodies { + guard let rigidBody = scene.get(component: RigidBodyComponent.self, for: entity), + rigidBody.motionType == .kinematic, + let transform = scene.get(component: LocalTransformComponent.self, for: entity) + else { + continue + } + kinematicEntities.append(entity) + kinematicTransforms.append( + PhysicsBodyTransform(position: transform.position, orientation: transform.rotation) + ) + } + + guard !kinematicEntities.isEmpty else { return } + + kinematicEntities.withUnsafeBufferPointer { entities in + kinematicTransforms.withUnsafeBufferPointer { transforms in + backend.writeKinematicTargets( + PhysicsBodyWriteBatch(entities: entities, transforms: transforms) + ) + } + } + } + + private func pullTransforms(from backend: any PhysicsBackend) { + let capacity = knownBodies.count + guard capacity > 0 else { return } + + if readbackEntities.count < capacity { + readbackEntities = [EntityID](repeating: 0, count: capacity) + readbackTransforms = [PhysicsBodyTransform]( + repeating: PhysicsBodyTransform( + position: .zero, + orientation: simd_quatf(ix: 0, iy: 0, iz: 0, r: 1) + ), + count: capacity + ) + } + + var written = 0 + readbackEntities.withUnsafeMutableBufferPointer { entities in + readbackTransforms.withUnsafeMutableBufferPointer { transforms in + written = backend.readActiveTransforms( + into: PhysicsTransformReadBatch(entities: entities, transforms: transforms) + ) + } + } + + guard written > 0 else { return } + + for index in 0 ..< min(written, capacity) { + let entity = readbackEntities[index] + guard knownBodies.contains(entity), + let rigidBody = scene.get(component: RigidBodyComponent.self, for: entity), + rigidBody.motionType == .dynamic, + let transform = scene.get(component: LocalTransformComponent.self, for: entity) + else { + continue + } + + transform.position = readbackTransforms[index].position + transform.rotation = readbackTransforms[index].orientation + transform.transformDirty = true + anyTransformDirty = true + } + } +} diff --git a/Sources/UntoldEngine/Systems/PhysicsSystem.swift b/Sources/UntoldEngine/Systems/PhysicsSystem.swift index 1e2e8d6fb..1bf22105f 100644 --- a/Sources/UntoldEngine/Systems/PhysicsSystem.swift +++ b/Sources/UntoldEngine/Systems/PhysicsSystem.swift @@ -99,13 +99,14 @@ public func updatePhysicsSystem(deltaTime: Float) { let physicsId = getComponentId(for: PhysicsComponents.self) let transformId = getComponentId(for: LocalTransformComponent.self) let entities = queryEntitiesWithComponentIds([kineticId, physicsId, transformId], in: scene) + let gravity = PhysicsCoordinator.shared.worldGravity for entity in entities { if isPhysicsComponentPaused(entityId: entity) { continue } - addGravity(entityId: entity, gravity: simd_float3(0.0, -9.8, 0.0)) // add gravity + addGravity(entityId: entity, gravity: gravity) addDrag(entityId: entity, deltaTime: deltaTime) accumulateForces(entityId: entity, deltaTime: deltaTime) // Apply accumulated forces to acceleration accumulateMoment(entityId: entity, deltaTime: deltaTime) diff --git a/Tests/UntoldEngineTests/PhysicsCoordinatorTests.swift b/Tests/UntoldEngineTests/PhysicsCoordinatorTests.swift new file mode 100644 index 000000000..1e5610347 --- /dev/null +++ b/Tests/UntoldEngineTests/PhysicsCoordinatorTests.swift @@ -0,0 +1,377 @@ +// +// PhysicsCoordinatorTests.swift +// UntoldEngineTests +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import simd +@testable import UntoldEngine +import XCTest + +private final class RecordingPhysicsBackend: PhysicsBackend, @unchecked Sendable { + let id: String + let capabilities: PhysicsCapabilities = [] + + private(set) var configurations: [PhysicsWorldConfiguration] = [] + private(set) var addedBodies: [(entity: EntityID, descriptor: PhysicsBodyDescriptor)] = [] + private(set) var removedBodies: [EntityID] = [] + private(set) var stepDeltas: [Float] = [] + private(set) var kinematicWrites: [[(entity: EntityID, transform: PhysicsBodyTransform)]] = [] + private(set) var drainCount = 0 + private(set) var callOrder: [String] = [] + + /// Transforms handed back on the next `readActiveTransforms`. + var pendingReadback: [(entity: EntityID, transform: PhysicsBodyTransform)] = [] + var droppedEventsToReport = 0 + + init(id: String = "com.example.recordingphysics") { + self.id = id + } + + func configure(_ config: PhysicsWorldConfiguration) { + configurations.append(config) + callOrder.append("configure") + } + + func didAddBody(entity: EntityID, descriptor: PhysicsBodyDescriptor) { + addedBodies.append((entity, descriptor)) + callOrder.append("addBody") + } + + func didRemoveBody(entity: EntityID) { + removedBodies.append(entity) + callOrder.append("removeBody") + } + + func step(deltaTime: Float) { + stepDeltas.append(deltaTime) + callOrder.append("step") + } + + func drainEvents(into sink: any PhysicsEventSink) { + drainCount += 1 + callOrder.append("drain") + if droppedEventsToReport > 0 { + sink.reportDroppedEvents(count: droppedEventsToReport) + } + } + + func writeKinematicTargets(_ batch: PhysicsBodyWriteBatch) { + var entries: [(EntityID, PhysicsBodyTransform)] = [] + for index in 0 ..< batch.entities.count { + entries.append((batch.entities[index], batch.transforms[index])) + } + kinematicWrites.append(entries) + callOrder.append("writeKinematic") + } + + func readActiveTransforms(into batch: PhysicsTransformReadBatch) -> Int { + callOrder.append("read") + let count = min(pendingReadback.count, batch.capacity) + for index in 0 ..< count { + batch.entities[index] = pendingReadback[index].entity + batch.transforms[index] = pendingReadback[index].transform + } + return count + } +} + +private struct RecordingBackendPlugin: PhysicsBackendPlugin { + let manifest: PhysicsBackendPluginManifest + let backend: RecordingPhysicsBackend + + init(pluginID: String = "com.example.recordingphysics") { + manifest = PhysicsBackendPluginManifest( + id: pluginID, + displayName: "Recording Physics", + version: PhysicsBackendVersion(major: 1, minor: 0, patch: 0), + requiredAPIVersion: .current + ) + backend = RecordingPhysicsBackend(id: pluginID) + } + + func makeBackend() -> any PhysicsBackend { + backend + } +} + +@MainActor +final class PhysicsCoordinatorTests: XCTestCase { + override func setUp() async throws { + resetEngineTestState() + } + + override func tearDown() { + PhysicsBackendRegistry.shared.resetForTesting() + super.tearDown() + } + + private func makeContext() -> EngineExtensionUpdateContext { + EngineExtensionUpdateContext( + viewport: SIMD2(640, 480), + immersionStyle: .none, + frameIndex: 0, + currentEye: 0, + isPrimaryEye: true + ) + } + + private func tick(_ deltaTime: Float = 1.0 / 60.0) { + PhysicsCoordinator.shared.fixedUpdate(deltaTime: deltaTime, context: makeContext()) + } + + @discardableResult + private func installRecordingPlugin() -> RecordingPhysicsBackend { + let plugin = RecordingBackendPlugin() + XCTAssertEqual(PhysicsBackendRegistry.shared.install(plugin), .installed) + return plugin.backend + } + + private func makeBodyEntity( + motionType: PhysicsMotionType = .dynamic, + position: simd_float3 = .zero + ) -> EntityID { + let entityId = createEntity() + registerComponent(entityId: entityId, componentType: RigidBodyComponent.self) + registerComponent(entityId: entityId, componentType: ColliderComponent.self) + scene.get(component: RigidBodyComponent.self, for: entityId)?.motionType = motionType + scene.get(component: LocalTransformComponent.self, for: entityId)?.position = position + return entityId + } + + // MARK: - Scheduling + + func testInstallSchedulesCoordinatorAndUninstallRemovesIt() { + XCTAssertFalse( + EngineExtensionRegistry.shared.registeredIDs().contains(PhysicsCoordinator.shared.id) + ) + + installRecordingPlugin() + XCTAssertTrue( + EngineExtensionRegistry.shared.registeredIDs().contains(PhysicsCoordinator.shared.id) + ) + + XCTAssertTrue(PhysicsBackendRegistry.shared.uninstall(id: "com.example.recordingphysics")) + XCTAssertFalse( + EngineExtensionRegistry.shared.registeredIDs().contains(PhysicsCoordinator.shared.id) + ) + } + + func testInstallConfiguresBackendWithCurrentWorldConfiguration() { + var config = PhysicsWorldConfiguration() + config.gravity = simd_float3(0.0, -3.7, 0.0) + PhysicsCoordinator.shared.setWorldConfiguration(config) + + let backend = installRecordingPlugin() + XCTAssertEqual(backend.configurations.count, 1) + XCTAssertEqual(backend.configurations.first?.gravity, simd_float3(0.0, -3.7, 0.0)) + } + + func testSetWorldConfigurationReconfiguresActiveBackend() { + let backend = installRecordingPlugin() + XCTAssertEqual(backend.configurations.count, 1) + + var config = PhysicsWorldConfiguration() + config.gravity = simd_float3(0.0, -1.6, 0.0) + PhysicsCoordinator.shared.setWorldConfiguration(config) + + XCTAssertEqual(backend.configurations.count, 2) + XCTAssertEqual(backend.configurations.last?.gravity, simd_float3(0.0, -1.6, 0.0)) + } + + func testFixedUpdateWithoutBackendIsInert() { + _ = makeBodyEntity() + tick() + // Nothing to assert beyond "no trap": no backend, no coordinator work. + XCTAssertFalse(PhysicsBackendRegistry.shared.isLockedForRuntime) + } + + // MARK: - Runtime lock + + func testFirstSimulatedSubstepLocksRegistry() { + installRecordingPlugin() + XCTAssertFalse(PhysicsBackendRegistry.shared.isLockedForRuntime) + + tick() + XCTAssertTrue(PhysicsBackendRegistry.shared.isLockedForRuntime) + + XCTAssertFalse(PhysicsBackendRegistry.shared.uninstall(id: "com.example.recordingphysics")) + if case .rejected = PhysicsBackendRegistry.shared.install(RecordingBackendPlugin()) { + } else { + XCTFail("Install after the first simulated substep should be rejected") + } + } + + // MARK: - Body lifecycle + + func testBodyEntityIsAddedWithDescriptorSnapshot() throws { + let backend = installRecordingPlugin() + let entityId = makeBodyEntity(position: simd_float3(1.0, 2.0, 3.0)) + + let rigidBody = scene.get(component: RigidBodyComponent.self, for: entityId) + rigidBody?.mass = 4.0 + rigidBody?.layer = 2 + rigidBody?.initialLinearVelocity = simd_float3(0.0, 0.0, 5.0) + let collider = scene.get(component: ColliderComponent.self, for: entityId) + collider?.shape = .box(halfExtents: simd_float3(0.5, 0.5, 0.5)) + collider?.isTrigger = true + + tick() + + XCTAssertEqual(backend.addedBodies.count, 1) + let added = try XCTUnwrap(backend.addedBodies.first) + XCTAssertEqual(added.entity, entityId) + XCTAssertEqual(added.descriptor.motionType, .dynamic) + XCTAssertEqual(added.descriptor.mass, 4.0) + XCTAssertEqual(added.descriptor.layer, 2) + XCTAssertEqual(added.descriptor.position, simd_float3(1.0, 2.0, 3.0)) + XCTAssertEqual(added.descriptor.linearVelocity, simd_float3(0.0, 0.0, 5.0)) + XCTAssertEqual(added.descriptor.collider.shape, .box(halfExtents: simd_float3(0.5, 0.5, 0.5))) + XCTAssertTrue(added.descriptor.collider.isTrigger) + + // Known bodies are not re-added on later substeps. + tick() + XCTAssertEqual(backend.addedBodies.count, 1) + } + + func testEntityWithoutColliderIsNotABody() { + let backend = installRecordingPlugin() + let entityId = createEntity() + registerComponent(entityId: entityId, componentType: RigidBodyComponent.self) + + tick() + XCTAssertTrue(backend.addedBodies.isEmpty) + } + + func testDestroyedEntityIsRemovedFromBackend() { + let backend = installRecordingPlugin() + let entityId = makeBodyEntity() + + tick() + XCTAssertEqual(backend.addedBodies.count, 1) + + destroyEntity(entityId: entityId) + scene.finalizePendingDestroys() + + tick() + XCTAssertEqual(backend.removedBodies, [entityId]) + } + + func testRemovingPhysicsComponentsRemovesBody() { + let backend = installRecordingPlugin() + let entityId = makeBodyEntity() + + tick() + XCTAssertEqual(backend.addedBodies.count, 1) + + scene.remove(component: RigidBodyComponent.self, from: entityId) + + tick() + XCTAssertEqual(backend.removedBodies, [entityId]) + } + + // MARK: - Transform transfer + + func testDynamicTransformReadbackUpdatesLocalTransform() throws { + let backend = installRecordingPlugin() + let entityId = makeBodyEntity(position: simd_float3(0.0, 10.0, 0.0)) + + let newOrientation = simd_quatf(angle: .pi / 2, axis: simd_float3(0.0, 1.0, 0.0)) + backend.pendingReadback = [( + entityId, + PhysicsBodyTransform(position: simd_float3(0.0, 9.5, 0.0), orientation: newOrientation) + )] + + anyTransformDirty = false + tick() + + let transform = try XCTUnwrap(scene.get(component: LocalTransformComponent.self, for: entityId)) + XCTAssertEqual(transform.position, simd_float3(0.0, 9.5, 0.0)) + XCTAssertEqual(transform.rotation.vector, newOrientation.vector) + XCTAssertTrue(transform.transformDirty) + XCTAssertTrue(anyTransformDirty) + } + + func testReadbackIgnoresKinematicAndUnknownEntities() throws { + let backend = installRecordingPlugin() + let kinematicId = makeBodyEntity(motionType: .kinematic, position: simd_float3(1.0, 0.0, 0.0)) + let strangerId: EntityID = 987_654 + + backend.pendingReadback = [ + (kinematicId, PhysicsBodyTransform( + position: simd_float3(5.0, 5.0, 5.0), + orientation: simd_quatf(ix: 0, iy: 0, iz: 0, r: 1) + )), + (strangerId, PhysicsBodyTransform( + position: simd_float3(6.0, 6.0, 6.0), + orientation: simd_quatf(ix: 0, iy: 0, iz: 0, r: 1) + )), + ] + + tick() + + let transform = try XCTUnwrap(scene.get(component: LocalTransformComponent.self, for: kinematicId)) + XCTAssertEqual(transform.position, simd_float3(1.0, 0.0, 0.0)) + } + + func testKinematicTargetsAreBatchWritten() throws { + let backend = installRecordingPlugin() + let kinematicId = makeBodyEntity(motionType: .kinematic, position: simd_float3(2.0, 0.0, 2.0)) + _ = makeBodyEntity(motionType: .dynamic) + + tick() + + XCTAssertEqual(backend.kinematicWrites.count, 1) + let write = try XCTUnwrap(backend.kinematicWrites.first) + XCTAssertEqual(write.count, 1) + XCTAssertEqual(write.first?.entity, kinematicId) + XCTAssertEqual(write.first?.transform.position, simd_float3(2.0, 0.0, 2.0)) + } + + // MARK: - Step orchestration + + func testSubstepCallOrderAndEventDrain() { + let backend = installRecordingPlugin() + _ = makeBodyEntity(motionType: .kinematic) + backend.droppedEventsToReport = 3 + + tick(1.0 / 60.0) + + XCTAssertEqual(backend.callOrder, ["configure", "addBody", "writeKinematic", "step", "read", "drain"]) + XCTAssertEqual(backend.stepDeltas, [1.0 / 60.0]) + XCTAssertEqual(backend.drainCount, 1) + XCTAssertEqual(PhysicsCoordinator.shared.eventSink.droppedEventCount, 3) + } + + // MARK: - Built-in integrator gravity seam + + func testBuiltInIntegratorReadsConfigurableGravity() { + let entityId = createEntity() + registerComponent(entityId: entityId, componentType: PhysicsComponents.self) + registerComponent(entityId: entityId, componentType: KineticComponent.self) + setMass(entityId: entityId, mass: 1.0) + setGravityScale(entityId: entityId, gravityScale: 1.0) + + var config = PhysicsWorldConfiguration() + config.gravity = .zero + PhysicsCoordinator.shared.setWorldConfiguration(config) + + updatePhysicsSystem(deltaTime: 1.0 / 60.0) + XCTAssertEqual(getVelocity(entityId: entityId), .zero) + + PhysicsCoordinator.shared.setWorldConfiguration(PhysicsWorldConfiguration()) + updatePhysicsSystem(deltaTime: 1.0 / 60.0) + XCTAssertLessThan(getVelocity(entityId: entityId).y, 0.0) + } + + func testDefaultWorldConfigurationMatchesLegacyGravity() { + XCTAssertEqual( + PhysicsCoordinator.shared.worldConfiguration().gravity, + simd_float3(0.0, -9.8, 0.0) + ) + } +} From 0e088a9333ca2f6cda19b936b83977f61b27764e Mon Sep 17 00:00:00 2001 From: Untold Engine Date: Wed, 5 Aug 2026 08:03:26 -0700 Subject: [PATCH 2/8] [Feature] Mesh and Gaussian share streaming path [Chores] Formatted files [Patch] Fixed the guassian-mesh occlusion [Patch] Improved API function call --- Sources/CShaderTypes/ShaderTypes.h | 4 + Sources/Sandbox/GameScene.swift | 18 +- Sources/UntoldEngine/ECS/Components.swift | 21 +- .../Renderer/RenderInitializer.swift | 12 + .../UntoldEngine/Renderer/RenderPasses.swift | 36 ++ .../Renderer/RenderResources.swift | 6 + Sources/UntoldEngine/Shaders/Gaussians.metal | 27 +- .../UntoldEngine/Systems/GaussianSystem.swift | 12 + ...tryStreamingSystem+GaussianStreaming.swift | 126 ++++++ ...eometryStreamingSystem+MeshStreaming.swift | 10 + ...eometryStreamingSystem+TileStreaming.swift | 45 ++ .../Systems/RegistrationSystem.swift | 190 ++++++-- .../UntoldEngineKernels-ios.air | Bin 164384 -> 164768 bytes .../UntoldEngineKernels-ios.metallib | Bin 546191 -> 547151 bytes .../UntoldEngineKernels-iossim.metallib | Bin 548735 -> 549695 bytes .../UntoldEngineKernels-tvos.air | Bin 164384 -> 164768 bytes .../UntoldEngineKernels-tvos.metallib | Bin 546495 -> 547471 bytes .../UntoldEngineKernels-tvossim.air | Bin 164416 -> 164800 bytes .../UntoldEngineKernels-tvossim.metallib | Bin 549039 -> 549999 bytes .../UntoldEngineKernels-xros.air | Bin 164384 -> 164768 bytes .../UntoldEngineKernels-xros.metallib | Bin 546527 -> 547503 bytes .../UntoldEngineKernels-xrossim.air | Bin 164416 -> 164800 bytes .../UntoldEngineKernels-xrossim.metallib | Bin 549071 -> 550031 bytes .../UntoldEngineKernels.metallib | Bin 544463 -> 545439 bytes .../GaussianRenderingTest.swift | 137 ++++++ .../GaussianStreamingTest.swift | 423 ++++++++++++++++++ docs/API/UsingGaussianSystem.md | 82 ++++ 27 files changed, 1118 insertions(+), 31 deletions(-) create mode 100644 Sources/UntoldEngine/Systems/GeometryStreamingSystem+GaussianStreaming.swift create mode 100644 Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift diff --git a/Sources/CShaderTypes/ShaderTypes.h b/Sources/CShaderTypes/ShaderTypes.h index c96c5c50c..97dedb748 100644 --- a/Sources/CShaderTypes/ShaderTypes.h +++ b/Sources/CShaderTypes/ShaderTypes.h @@ -511,6 +511,10 @@ typedef enum{ gaussianTBDRRenderLocalCameraIndex, }GaussianTBDRRenderBufferIndices; +typedef enum{ + gaussianTBDRDrawOpaqueDepthTextureIndex = 0, + }GaussianTBDRDrawTextureIndices; + typedef enum{ outputTransformPassEncodingModeIndex }OutputTransformBufferIndices; diff --git a/Sources/Sandbox/GameScene.swift b/Sources/Sandbox/GameScene.swift index 8f54c4fa6..442777197 100644 --- a/Sources/Sandbox/GameScene.swift +++ b/Sources/Sandbox/GameScene.swift @@ -49,12 +49,24 @@ */ // Uncomment to render a streamed scene - /* + let sceneRoot = createEntity() setEntityStreamScene(entityId: sceneRoot, url: URL(string: "https://d8pyi1c08k1w.cloudfront.net/dungeon3/dungeon3.json")!){ success in - setSceneReady(success) + //setSceneReady(success) + } + + let streamSplat = createEntity() + + Task { + await setEntityGaussianAsync( + entityId: streamSplat, + filename: "/Users/haroldserrano/Downloads/Pooltable", + withExtension: "ply" + ) { success in + rotateTo(entityId: streamSplat, angle: 180.0, axis: simd_float3(1.0,0.0,0.0)) + setSceneReady(success) + } } - */ } private func configureEngineSystems() { diff --git a/Sources/UntoldEngine/ECS/Components.swift b/Sources/UntoldEngine/ECS/Components.swift index c7d5753b6..54f7e9d0e 100644 --- a/Sources/UntoldEngine/ECS/Components.swift +++ b/Sources/UntoldEngine/ECS/Components.swift @@ -780,6 +780,18 @@ public enum MeshStreamingState: String { case unloading // Being unloaded } +/// Kind of asset a StreamingComponent loads/unloads. +/// +/// Determines which loader `GeometryStreamingSystem.loadMesh`/`unloadMesh` dispatch to. +/// `.mesh` goes through the existing `.untold`/OCC mesh pipeline; `.gaussianSplat` goes +/// through `setEntityGaussianAsync`/`unloadGaussian`. The distance/radius scheduling, +/// concurrency, and memory-budget eviction logic in `GeometryStreamingSystem` is shared +/// across both kinds. +public enum StreamingAssetKind { + case mesh + case gaussianSplat +} + /// Component that marks an entity for geometry streaming public class StreamingComponent: Component { /// Distance from camera at which mesh starts loading @@ -794,12 +806,15 @@ public class StreamingComponent: Component { /// Filename of the asset (without extension) public var assetFilename: String = "" - /// File extension (e.g., "usdz") + /// File extension (e.g., "usdz", or "ply" for gaussian splats) public var assetExtension: String = "" /// Optional: specific mesh name within the asset public var assetName: String? + /// Which loader this entity streams through. See `StreamingAssetKind`. + public var assetKind: StreamingAssetKind = .mesh + /// Current streaming state public var state: MeshStreamingState = .unloaded @@ -817,7 +832,8 @@ public class StreamingComponent: Component { withExtension ext: String, streamingRadius: Float = 100.0, unloadRadius: Float = 150.0, - priority: Int = 0 + priority: Int = 0, + assetKind: StreamingAssetKind = .mesh ) { self.init() assetFilename = filename @@ -825,6 +841,7 @@ public class StreamingComponent: Component { self.streamingRadius = streamingRadius self.unloadRadius = unloadRadius self.priority = priority + self.assetKind = assetKind } } diff --git a/Sources/UntoldEngine/Renderer/RenderInitializer.swift b/Sources/UntoldEngine/Renderer/RenderInitializer.swift index d1ad97bb5..348f5e3b8 100644 --- a/Sources/UntoldEngine/Renderer/RenderInitializer.swift +++ b/Sources/UntoldEngine/Renderer/RenderInitializer.swift @@ -790,6 +790,18 @@ func initTextureResources() { storageMode: .private ) + // Gaussian pass reads a snapshot of the opaque depth to occlude splats behind + // walls/geometry. Copied fresh each frame from depthMap (see gaussianExecution). + textureResources.gaussianOpaqueDepthSnapshot = createTexture( + device: renderInfo.device, + label: "Gaussian Opaque Depth Snapshot", + pixelFormat: renderInfo.depthPixelFormat, + width: viewportWidth, + height: viewportHeight, + usage: [.shaderRead, .renderTarget], + storageMode: .private + ) + // Deferred Depth Texture textureResources.deferredDepthMap = createTexture( device: renderInfo.device, diff --git a/Sources/UntoldEngine/Renderer/RenderPasses.swift b/Sources/UntoldEngine/Renderer/RenderPasses.swift index 3d8c91fda..02a57cc3c 100644 --- a/Sources/UntoldEngine/Renderer/RenderPasses.swift +++ b/Sources/UntoldEngine/Renderer/RenderPasses.swift @@ -4588,6 +4588,31 @@ public enum RenderPasses { renderPassDescriptor.tileHeight = 32 renderPassDescriptor.imageblockSampleLength = initializePipelineState.imageblockSampleLength + // Snapshot the opaque depth before the pass so splats can be occluded by it. + // depthMap is also this pass's own .load'ed depth attachment below, and this + // engine never binds the same texture as both an attachment and a + // separately-sampled argument in one encoder (see copyOpaqueDepthForHZBExecution + // for the same precaution) — so the draw stage reads this copy instead. + if let sourceDepth = textureResources.depthMap, + let opaqueDepthSnapshot = textureResources.gaussianOpaqueDepthSnapshot + { + let width = min(sourceDepth.width, opaqueDepthSnapshot.width) + let height = min(sourceDepth.height, opaqueDepthSnapshot.height) + if width > 0, height > 0, let blitEncoder = commandBuffer.makeBlitCommandEncoder() { + blitEncoder.label = "Copy Opaque Depth for Gaussian Occlusion" + blitEncoder.copy( + from: sourceDepth, + sourceSlice: 0, sourceLevel: 0, + sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0), + sourceSize: MTLSize(width: width, height: height, depth: 1), + to: opaqueDepthSnapshot, + destinationSlice: 0, destinationLevel: 0, + destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0) + ) + blitEncoder.endEncoding() + } + } + guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { handleError(.renderPassCreationFailed, "Gaussian Pass") return @@ -4607,6 +4632,17 @@ public enum RenderPasses { renderEncoder.setRenderPipelineState(drawPipelineState) renderEncoder.setDepthStencilState(drawPipeline.depthState) + renderEncoder.setFragmentTexture( + textureResources.gaussianOpaqueDepthSnapshot, + index: Int(gaussianTBDRDrawOpaqueDepthTextureIndex.rawValue) + ) + var gaussianDrawReverseZ = renderInfo.reverseZEnabled + renderEncoder.setFragmentBytes( + &gaussianDrawReverseZ, + length: MemoryLayout.stride, + index: Int(gaussianTBDRRenderReverseZIndex.rawValue) + ) + let transformId = getComponentId(for: WorldTransformComponent.self) let gaussianId = getComponentId(for: GaussianComponent.self) let entities = queryEntitiesWithComponentIds([transformId, gaussianId], in: scene) diff --git a/Sources/UntoldEngine/Renderer/RenderResources.swift b/Sources/UntoldEngine/Renderer/RenderResources.swift index 59f7b5a1a..d042dc384 100644 --- a/Sources/UntoldEngine/Renderer/RenderResources.swift +++ b/Sources/UntoldEngine/Renderer/RenderResources.swift @@ -172,6 +172,12 @@ public struct TextureResources { public var msaaDepthMap: MTLTexture? public var msaaDeferredColorMap: MTLTexture? public var hzbSourceDepthMap: MTLTexture? + /// Snapshot of the opaque scene's depth, copied before the Gaussian pass so splats + /// can be occluded by opaque geometry. A dedicated copy (rather than reading + /// `depthMap` directly) mirrors `hzbSourceDepthMap`'s pattern — `depthMap` is also + /// this pass's own `.load`ed depth attachment, and no other pass in this engine + /// binds the same texture as both an attachment and a separately-sampled argument. + public var gaussianOpaqueDepthSnapshot: MTLTexture? public var environmentColorMap: MTLTexture? // deferred diff --git a/Sources/UntoldEngine/Shaders/Gaussians.metal b/Sources/UntoldEngine/Shaders/Gaussians.metal index 60f8c7cee..d3500f195 100644 --- a/Sources/UntoldEngine/Shaders/Gaussians.metal +++ b/Sources/UntoldEngine/Shaders/Gaussians.metal @@ -352,7 +352,9 @@ vertex GaussianOutData vertexGaussianTBDRShader( fragment GaussianTBDRFragmentStore fragmentGaussianTBDRShader( GaussianOutData in [[stage_in]], - GaussianTBDRFragmentValues previousValues [[imageblock_data]]) + GaussianTBDRFragmentValues previousValues [[imageblock_data]], + depth2d opaqueDepth [[texture(gaussianTBDRDrawOpaqueDepthTextureIndex)]], + constant bool &reverseZ [[buffer(gaussianTBDRRenderReverseZIndex)]]) { GaussianTBDRFragmentStore out; @@ -360,6 +362,29 @@ fragment GaussianTBDRFragmentStore fragmentGaussianTBDRShader( discard_fragment(); } + // Occlude against opaque geometry already in the depth buffer (a snapshot taken + // before this pass — see gaussianExecution). in.position.z is already normalized + // device depth from the same projection the opaque pass used, so it's directly + // comparable to the stored value with no linearization needed. A small bias avoids + // a hard cutoff exactly at surface intersections. Background pixels hold the clear + // value (the farthest depth in either convention), so splats over empty background + // are never occluded without any special-casing. + float storedOpaqueDepth = opaqueDepth.read(uint2(in.position.xy)); + float splatDepth = in.position.z; + const float depthBias = 0.0005f; + bool occludedByOpaque = reverseZ + ? (splatDepth + depthBias < storedOpaqueDepth) + : (splatDepth > storedOpaqueDepth + depthBias); + if (occludedByOpaque) { + // discard_fragment() only suppresses standard color/depth attachment writes — + // it does NOT stop this function from computing and returning a value for the + // custom imageblock struct below, so relying on it alone here would let an + // occluded splat's contribution reach the accumulator anyway. Return the + // accumulator unchanged instead, so an occluded splat contributes nothing. + out.values = previousValues; + return out; + } + const float projYSign = 1.0f; float2 d = calcScreenSpaceDelta(in.position.xy, in.coordxy, projYSign); float power = calcPowerFromConic(in.conic, d); diff --git a/Sources/UntoldEngine/Systems/GaussianSystem.swift b/Sources/UntoldEngine/Systems/GaussianSystem.swift index db534bb1c..30c42b0c0 100644 --- a/Sources/UntoldEngine/Systems/GaussianSystem.swift +++ b/Sources/UntoldEngine/Systems/GaussianSystem.swift @@ -27,6 +27,7 @@ private func activeGaussianSortCount(_ component: GaussianComponent) -> Int { } private struct GaussianVisibleCountUpdate: @unchecked Sendable { + let entityId: EntityID let component: GaussianComponent let visibleCount: MTLBuffer let splatCount: UInt @@ -154,6 +155,7 @@ public func executeGaussianFrustumCulling(_ commandBuffer: MTLCommandBuffer) { visibleCountUpdates.append( GaussianVisibleCountUpdate( + entityId: entityId, component: gaussianComponent, visibleCount: visibleCount, splatCount: gaussianComponent.splatCount @@ -168,6 +170,16 @@ public func executeGaussianFrustumCulling(_ commandBuffer: MTLCommandBuffer) { for update in updates { let count = update.visibleCount.contents().load(as: UInt32.self) update.component.visibleSplatCountForRendering = min(UInt(count), update.splatCount) + + // Streamed splat entities carry no RenderComponent, so they're structurally + // excluded from the RenderComponent-keyed culling query that normally feeds + // MemoryBudgetManager.markUsed (see CullingSystem.swift). Without this, a loaded + // splat's lastUsedFrame would never advance and evictLRU would treat it as the + // stalest entity in the budget regardless of actual visibility. Mark used only + // when at least one splat survived this frame's frustum test. + if update.component.visibleSplatCountForRendering > 0 { + MemoryBudgetManager.shared.markUsed(entityId: update.entityId) + } } } diff --git a/Sources/UntoldEngine/Systems/GeometryStreamingSystem+GaussianStreaming.swift b/Sources/UntoldEngine/Systems/GeometryStreamingSystem+GaussianStreaming.swift new file mode 100644 index 000000000..f8ecd50df --- /dev/null +++ b/Sources/UntoldEngine/Systems/GeometryStreamingSystem+GaussianStreaming.swift @@ -0,0 +1,126 @@ +// +// GeometryStreamingSystem+GaussianStreaming.swift +// UntoldEngine +// +// Gaussian-splat streaming load dispatch and unload. The actual asset load goes through +// the shared, public setEntityGaussianAsync (RegistrationSystem.swift) — gaussians have no +// cache/OCC layer analogous to MeshResourceManager, so unlike mesh streaming there is no +// separate streaming-only load helper, just the dispatch/completion wrapper below. +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import Foundation + +extension GeometryStreamingSystem { + /// Dispatches the async load for a streamed Gaussian-splat entity and installs the + /// completion handling (state transition, residency event, monitor bookkeeping, + /// active-load slot release). Called from `loadMesh` once it has already reserved the + /// load slot and recorded scheduler-latency instrumentation — mirrors the structure of + /// `loadMesh`'s own mesh-path `Task`, but calls the shared `setEntityGaussianAsync` + /// instead of the mesh-only `loadMeshAsync`/`reloadLODEntity`, and skips + /// `incrementParentTileOCCCount` — that counter tracks progressive upload of a tile's own + /// OCC sub-meshes (`TileComponent.totalOCCStubs`), which splat props never contribute to; + /// incrementing it here would inflate the numerator past a denominator it never grew, + /// corrupting the tile's `visualState` completion fraction. + func loadGaussianStreamingEntity(entityId: EntityID, streaming: StreamingComponent, isNearBand: Bool) { + let filename = streaming.assetFilename + let ext = streaming.assetExtension + + let task = Task { + let asyncLoadStart = CFAbsoluteTimeGetCurrent() + let success = await setEntityGaussianAsync(entityId: entityId, filename: filename, withExtension: ext) + let asyncLoadMs = (CFAbsoluteTimeGetCurrent() - asyncLoadStart) * 1000.0 + + var applyMs: Double = 0 + withWorldMutationGate { + let applyStart = CFAbsoluteTimeGetCurrent() + + // Guard against the cooperative-cancellation race, same as loadMesh's mesh path: + // unloadGaussian may have freed this entity while the load was in flight. + guard scene.exists(entityId) else { + releaseActiveLoad(entityId: entityId) + if isNearBand { releaseNearBandLoad(entityId: entityId) } + return + } + + guard let s = scene.get(component: StreamingComponent.self, for: entityId), + s.state == .loading + else { + releaseActiveLoad(entityId: entityId) + if isNearBand { releaseNearBandLoad(entityId: entityId) } + return + } + + if success { + s.state = .loaded + s.lastVisibleFrame = currentFrame + + let event = AssetResidencyChangedEvent( + entityId: entityId, + assetURL: URL(fileURLWithPath: ""), + meshName: s.assetFilename, + isResident: true + ) + SystemEventBus.shared.queueResidencyChange(event) + markLoadedStreamingEntity(entityId) + SystemIntegrationMonitor.shared.recordStreamingLoad() + } else { + s.state = .unloaded + handleError(.meshStreamingFailed, entityId) + } + releaseActiveLoad(entityId: entityId) + if isNearBand { releaseNearBandLoad(entityId: entityId) } + applyMs = (CFAbsoluteTimeGetCurrent() - applyStart) * 1000.0 + } + recordLoadCompletion(success: success, asyncLoadMs: asyncLoadMs, applyMs: applyMs, wasLODReload: false) + } + + streaming.loadTask = task + } + + /// Tears down a streamed Gaussian-splat entity's GPU resources and removes it from the + /// shared memory budget. Mirrors `unloadMesh` (`GeometryStreamingSystem+MeshStreaming.swift`) + /// but skips the mesh-only bits (RenderComponent, LODComponent, MeshResourceManager, + /// BatchingSystem static-batch retirement) that don't apply to splats. + func unloadGaussian(entityId: EntityID) { + guard let streaming = scene.get(component: StreamingComponent.self, for: entityId), + streaming.state == .loaded + else { return } + + firstRangeTimestamps.removeValue(forKey: entityId) + + let unloadStart = CFAbsoluteTimeGetCurrent() + withWorldMutationGate { + streaming.state = .unloading + + streaming.loadTask?.cancel() + streaming.loadTask = nil + + removeEntityGaussian(entityId: entityId) + + // removeEntityGaussian already unregisters from MemoryBudgetManager, but the call + // is idempotent, so calling it here too keeps this function correct even if + // removeEntityGaussian's cleanup behavior changes later. + MemoryBudgetManager.shared.unregisterMesh(entityId: entityId) + + unmarkLoadedStreamingEntity(entityId) + + streaming.state = .unloaded + + let event = AssetResidencyChangedEvent( + entityId: entityId, + assetURL: URL(fileURLWithPath: ""), + meshName: streaming.assetFilename, + isResident: false + ) + SystemEventBus.shared.queueResidencyChange(event) + SystemIntegrationMonitor.shared.recordStreamingUnload() + } + let unloadMs = (CFAbsoluteTimeGetCurrent() - unloadStart) * 1000.0 + updateLastUnloadDuration(unloadMs) + } +} diff --git a/Sources/UntoldEngine/Systems/GeometryStreamingSystem+MeshStreaming.swift b/Sources/UntoldEngine/Systems/GeometryStreamingSystem+MeshStreaming.swift index e386c321f..958cfe90c 100644 --- a/Sources/UntoldEngine/Systems/GeometryStreamingSystem+MeshStreaming.swift +++ b/Sources/UntoldEngine/Systems/GeometryStreamingSystem+MeshStreaming.swift @@ -39,6 +39,11 @@ extension GeometryStreamingSystem { ) } + if streaming.assetKind == .gaussianSplat { + loadGaussianStreamingEntity(entityId: entityId, streaming: streaming, isNearBand: isNearBand) + return + } + let hasLOD = scene.get(component: LODComponent.self, for: entityId) != nil let filename = streaming.assetFilename @@ -369,6 +374,11 @@ extension GeometryStreamingSystem { streaming.state == .loaded else { return } + if streaming.assetKind == .gaussianSplat { + unloadGaussian(entityId: entityId) + return + } + // Clear first-detection timestamp so a future re-approach records a fresh baseline. firstRangeTimestamps.removeValue(forKey: entityId) diff --git a/Sources/UntoldEngine/Systems/GeometryStreamingSystem+TileStreaming.swift b/Sources/UntoldEngine/Systems/GeometryStreamingSystem+TileStreaming.swift index 460369592..908e99ae5 100644 --- a/Sources/UntoldEngine/Systems/GeometryStreamingSystem+TileStreaming.swift +++ b/Sources/UntoldEngine/Systems/GeometryStreamingSystem+TileStreaming.swift @@ -910,6 +910,18 @@ extension GeometryStreamingSystem { var result: [EntityID] = [] for childId in getEntityChildren(parentId: parentId) { if let streaming = scene.get(component: StreamingComponent.self, for: childId) { + // Gaussian-splat props are externally-attached, persistent content — unlike + // .mesh StreamingComponent children, they are never generated by the tile's + // own USDC/OCC parse pipeline (there is no such thing as a gaussian OCC + // sub-stub). They must survive the tile's own unload/reparse cycles rather + // than being torn down alongside its regenerable mesh descendants, so skip + // them entirely: do not cancel their load task, unmark them, collect them + // for destruction, or recurse into them. Their own streamingRadius/ + // unloadRadius continues to govern them independently via the normal + // StreamingComponent update() loop. + if streaming.assetKind == .gaussianSplat { + continue + } streaming.loadTask?.cancel() streaming.loadTask = nil unmarkLoadedStreamingEntity(childId) @@ -1048,3 +1060,36 @@ extension GeometryStreamingSystem { } } } + +// MARK: - Tile lookup by position + +/// Finds the tile stub entity (an entity carrying `TileComponent`, registered by +/// `setEntityStreamScene`) whose world-space bounding box contains `point`. +/// +/// Tile stub bounds are already in world space (tile transforms are identity), so a +/// direct AABB containment check against `LocalTransformComponent.boundingBox` is valid. +/// Useful for attaching an ad-hoc streaming prop (e.g. a Gaussian splat) to whichever +/// tile occupies a given world position — see `setEntityGaussianStreamable`. +/// +/// - Parameters: +/// - point: World-space position to test against each candidate tile's bounds. +/// - searchRadius: Octree query radius in world units. Only needs to cover roughly +/// one tile/room footprint; defaults to a generous 200m. +/// - Returns: The tile entity containing `point`, or `nil` if none is found within +/// `searchRadius`. +public func findTileEntity(containing point: simd_float3, searchRadius: Float = 200.0) -> EntityID? { + let nearby = OctreeSystem.shared.queryNear(point: point, radius: searchRadius) + for entityId in nearby { + guard scene.get(component: TileComponent.self, for: entityId) != nil, + let local = scene.get(component: LocalTransformComponent.self, for: entityId) + else { continue } + let (minB, maxB) = local.boundingBox + if point.x >= minB.x, point.x <= maxB.x, + point.y >= minB.y, point.y <= maxB.y, + point.z >= minB.z, point.z <= maxB.z + { + return entityId + } + } + return nil +} diff --git a/Sources/UntoldEngine/Systems/RegistrationSystem.swift b/Sources/UntoldEngine/Systems/RegistrationSystem.swift index e1244f199..c8a2f84f3 100644 --- a/Sources/UntoldEngine/Systems/RegistrationSystem.swift +++ b/Sources/UntoldEngine/Systems/RegistrationSystem.swift @@ -3195,10 +3195,28 @@ public func loadRawMesh( return Mesh.makeDefaultMesh() } -public func setEntityGaussian(entityId: EntityID, filename: String, withExtension: String) { +/// Built Metal resources for a parsed Gaussian splat asset, ready to attach to an entity. +/// Shared by `setEntityGaussian` (synchronous) and `setEntityGaussianAsync` (off-thread) so +/// there is a single implementation of the PLY-parse/buffer-build/SH-pack pipeline. +private struct GaussianLoadResult { + let splatCount: UInt + let gaussianSortedIndices: MTLBuffer + let gaussianVisibleIndices: MTLBuffer + let gaussianVisibleCount: MTLBuffer + let encodedSplatBuffer: MTLBuffer + let sphericalHarmonicsBuffer: MTLBuffer? + let sphericalHarmonicsMetadata: GaussianSHMetadata? + let spaceUniform: [MTLBuffer?] + /// Sum of all GPU buffer bytes above, for `MemoryBudgetManager` registration. + let estimatedGPUBytes: Int +} + +/// Reads a `.ply` Gaussian splat asset from disk and builds its GPU buffers. +/// Returns `nil` on any failure, calling `handleError` internally — callers just guard-return. +private func buildGaussianLoadResult(filename: String, withExtension: String) -> GaussianLoadResult? { guard let url = LoadingSystem.shared.resourceURL(forResource: filename, withExtension: withExtension, subResource: nil) else { handleError(.filenameNotFound, filename) - return + return nil } // Attempt to read Gaussian splats, handling errors internally @@ -3207,20 +3225,20 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio asset = try PLYReader.readGaussianAsset(from: url) } catch { handleError(.assetDataMissing, "Failed to read Gaussian splats from \(filename): \(error.localizedDescription)") - return + return nil } let splats = asset.splats // Check if we exceed the buffer capacity guard splats.count <= Int(maxNumOfGaussians) else { handleError(.bufferAllocationFailed, "Too many Gaussian splats: \(splats.count) exceeds maximum \(maxNumOfGaussians)") - return + return nil } let splatCount = UInt(splats.count) guard splatCount > 0 else { handleError(.assetDataMissing, "Gaussian splat file contains no vertices: \(filename)") - return + return nil } guard let gaussianSortedIndices = renderInfo.device.makeBuffer( @@ -3228,7 +3246,7 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio options: .storageModeShared ) else { handleError(.bufferAllocationFailed, "Gaussian sorted-index buffer is nil") - return + return nil } guard let gaussianVisibleIndices = renderInfo.device.makeBuffer( @@ -3236,7 +3254,7 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio options: .storageModeShared ) else { handleError(.bufferAllocationFailed, "Gaussian visible-index buffer is nil") - return + return nil } guard let gaussianVisibleCount = renderInfo.device.makeBuffer( @@ -3244,13 +3262,13 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio options: .storageModeShared ) else { handleError(.bufferAllocationFailed, "Gaussian visible-count buffer is nil") - return + return nil } gaussianVisibleCount.contents().storeBytes(of: UInt32(splatCount), as: UInt32.self) guard let encodedSplatBuffer = renderInfo.device.makeBuffer(length: MemoryLayout.stride * Int(splatCount), options: .storageModeShared) else { handleError(.bufferAllocationFailed, "Encoded Gaussian splat buffer is nil") - return + return nil } let encodedPointer = encodedSplatBuffer.contents().bindMemory( @@ -3269,7 +3287,7 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio } } catch { handleError(.assetDataMissing, "Failed to pack spherical harmonics from \(filename): \(error.localizedDescription)") - return + return nil } let sphericalHarmonicsBuffer: MTLBuffer? @@ -3281,7 +3299,7 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio ) guard sphericalHarmonicsBuffer != nil else { handleError(.bufferAllocationFailed, "Gaussian spherical-harmonics buffer is nil") - return + return nil } sphericalHarmonicsBuffer?.label = "Gaussian Spherical Harmonics" } else { @@ -3293,23 +3311,141 @@ public func setEntityGaussian(entityId: EntityID, filename: String, withExtensio options: [MTLResourceOptions.storageModeShared]) } + var estimatedGPUBytes = 0 + estimatedGPUBytes += gaussianSortedIndices.length + estimatedGPUBytes += gaussianVisibleIndices.length + estimatedGPUBytes += gaussianVisibleCount.length + estimatedGPUBytes += encodedSplatBuffer.length + estimatedGPUBytes += sphericalHarmonicsBuffer?.length ?? 0 + for buffer in spaceUniform { + estimatedGPUBytes += buffer.length + } + + return GaussianLoadResult( + splatCount: splatCount, + gaussianSortedIndices: gaussianSortedIndices, + gaussianVisibleIndices: gaussianVisibleIndices, + gaussianVisibleCount: gaussianVisibleCount, + encodedSplatBuffer: encodedSplatBuffer, + sphericalHarmonicsBuffer: sphericalHarmonicsBuffer, + sphericalHarmonicsMetadata: packedSphericalHarmonics?.metadata, + spaceUniform: spaceUniform, + estimatedGPUBytes: estimatedGPUBytes + ) +} + +/// Registers `GaussianComponent` on `entityId` from a built `GaussianLoadResult` and records +/// its GPU footprint with `MemoryBudgetManager`. Must be called from within a world-mutation +/// gate (`withWorldMutationGate`). +private func applyGaussianLoadResult(_ result: GaussianLoadResult, to entityId: EntityID) { + registerComponent(entityId: entityId, componentType: GaussianComponent.self) + + guard let gaussianComponent = scene.get(component: GaussianComponent.self, for: entityId) else { + handleError(.noRenderComponent, entityId) + return + } + + gaussianComponent.splatCount = result.splatCount + gaussianComponent.visibleSplatCountForRendering = result.splatCount + gaussianComponent.gaussianSortedIndices = result.gaussianSortedIndices + gaussianComponent.gaussianVisibleIndices = result.gaussianVisibleIndices + gaussianComponent.gaussianVisibleCount = result.gaussianVisibleCount + gaussianComponent.encodedSplatData = result.encodedSplatBuffer + gaussianComponent.sphericalHarmonicsData = result.sphericalHarmonicsBuffer + gaussianComponent.sphericalHarmonicsMetadata = result.sphericalHarmonicsMetadata + gaussianComponent.spaceUniform = result.spaceUniform + + MemoryBudgetManager.shared.registerMesh(entityId: entityId, meshSizeBytes: result.estimatedGPUBytes) +} + +public func setEntityGaussian(entityId: EntityID, filename: String, withExtension: String) { + guard let result = buildGaussianLoadResult(filename: filename, withExtension: withExtension) else { + return + } + withWorldMutationGate { - registerComponent(entityId: entityId, componentType: GaussianComponent.self) + applyGaussianLoadResult(result, to: entityId) + } +} - guard let gaussianComponent = scene.get(component: GaussianComponent.self, for: entityId) else { - handleError(.noRenderComponent, entityId) - return - } +/// Asynchronously reads and encodes a `.ply` Gaussian splat asset and attaches it to +/// `entityId` without blocking the main thread. Parsing, per-splat encoding, and spherical- +/// harmonics packing all run before the world-mutation gate is acquired; only the final +/// component registration runs under `withWorldMutationGate`, mirroring `setEntityMeshAsync`'s +/// pattern of keeping GPU resource work outside the gate. +/// +/// `async -> Bool` (rather than `setEntityMeshAsync`'s fire-and-forget/completion shape) so +/// `GeometryStreamingSystem.loadMesh` can `await` it directly from its own dispatch `Task`, +/// the same way it awaits the mesh path. Used both by direct callers that want a non-blocking +/// one-off load, and internally by the streaming system for distance-streamed splat props — +/// gaussians have no cache/OCC layer analogous to `MeshResourceManager`, so unlike mesh +/// streaming there is no separate streaming-only loader. +@discardableResult +public func setEntityGaussianAsync( + entityId: EntityID, + filename: String, + withExtension: String, + completion: (@Sendable (Bool) -> Void)? = nil +) async -> Bool { + guard let result = buildGaussianLoadResult(filename: filename, withExtension: withExtension) else { + completion?(false) + return false + } - gaussianComponent.splatCount = splatCount - gaussianComponent.visibleSplatCountForRendering = splatCount - gaussianComponent.gaussianSortedIndices = gaussianSortedIndices - gaussianComponent.gaussianVisibleIndices = gaussianVisibleIndices - gaussianComponent.gaussianVisibleCount = gaussianVisibleCount - gaussianComponent.encodedSplatData = encodedSplatBuffer - gaussianComponent.sphericalHarmonicsData = sphericalHarmonicsBuffer - gaussianComponent.sphericalHarmonicsMetadata = packedSphericalHarmonics?.metadata - gaussianComponent.spaceUniform = spaceUniform + withWorldMutationGate { + applyGaussianLoadResult(result, to: entityId) + } + + completion?(true) + return true +} + +/// Registers `entityId` as a distance-streamed Gaussian-splat prop, so +/// `GeometryStreamingSystem` loads/unloads it based on camera distance the same way it +/// does the surrounding tile geometry — rather than loading it immediately the way +/// `setEntityGaussian`/`setEntityGaussianAsync` do. +/// +/// `entityId` should already be positioned (e.g. via `translateTo`/`rotateTo`) before +/// calling this — the entity's current `LocalTransformComponent.position` is used to find +/// which tile it belongs to, via `findTileEntity(containing:)`. If no tile is found there, +/// this logs a warning and leaves `entityId` as a plain, non-streaming entity. +/// +/// `boundingBoxHalfExtent` has no default on purpose: `GeometryStreamingSystem`'s frustum +/// gate needs a real local-space volume on the entity before it ever loads (the splat's +/// true extent isn't known until the asset is parsed). A zero-size placeholder collapses +/// the gate to a single exact point, making re-streaming unreliable once the camera moves +/// away and back — pick a box roughly matching the prop's real-world size. +public func setEntityGaussianStreamable( + entityId: EntityID, + filename: String, + withExtension ext: String, + streamingRadius: Float = 100.0, + unloadRadius: Float = 150.0, + boundingBoxHalfExtent: simd_float3, + priority: Int = 0 +) { + guard let local = scene.get(component: LocalTransformComponent.self, for: entityId) else { + handleError(.noLocalTransformComponent, entityId) + return + } + + guard let tileEntity = findTileEntity(containing: local.position) else { + Logger.logWarning(message: "[RegistrationSystem] setEntityGaussianStreamable: no tile found containing position \(local.position) for entity \(entityId) — entity left non-streaming.") + return + } + + local.boundingBox = (min: -boundingBoxHalfExtent, max: boundingBoxHalfExtent) + + setParent(childId: entityId, parentId: tileEntity) + OctreeSystem.shared.registerEntity(entityId) + + if let streaming = scene.assign(to: entityId, component: StreamingComponent.self) { + streaming.assetKind = .gaussianSplat + streaming.assetFilename = filename + streaming.assetExtension = ext + streaming.streamingRadius = streamingRadius + streaming.unloadRadius = unloadRadius + streaming.priority = priority } } @@ -3535,6 +3671,10 @@ func removeEntityGaussian(entityId: EntityID) { gaussianComponent.visibleSplatCountForRendering = 0 gaussianComponent.spaceUniform.removeAll() scene.remove(component: GaussianComponent.self, from: entityId) + // Idempotent — safe to call again if `unloadGaussian` already unregistered this + // entity as part of a streaming unload. Keeps MemoryBudgetManager's ledger accurate + // for entities destroyed directly (e.g. a non-streamed setEntityGaussian caller). + MemoryBudgetManager.shared.unregisterMesh(entityId: entityId) } } diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-ios.air b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-ios.air index cadabab69629546448fe99cba40bc31756d40d84..c0ab4b40f7bd973021b9f09841925c1a9a4ccbd5 100644 GIT binary patch delta 48764 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 delta 48405 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 LBj-OM-`f3OetNPi delta 1454 zcmYLIeQXnD9Dd*H?e5lIx9dLGjnVbG4lQFb{h|y+cil>5%CLjYAi}s(6$We$ML|C7 z^%#XA`|<_{CTal%lt?xzOupBR0h$5R8AOC+41z#}Ofr&5aQL?V;hQ{p@;txi_x$tA zd-|;L=_zAMY5B||2r!Tcz^1Vv0R$Gdq`Zg$ef#WTtJz(hCGHu!QUaG#Zr%Onz}oXS zS9ROY_V>K@@jtIl{^`J!QumyB6li1Q8e0d(%d*SC%3fM6m)I9$L%ZT~C?`d}X7^*< zK_oX}Z6^@r$-|BXSncI_fSZsaub(s@8@L;nr^cw|&heB|zlchg-_euuD1zDPRsl67 z2a_{4gq)i~PKbaZs1R_31O%cZV$F=)P?Q5Z6z@%cA?yL)G)u#1|I=(;A*CZ@~M#nC3JYn)3+nZ4EeQrK0+`oPvUx?KlS z3)XlTX^`(62ig7LGmP~x`=?-D*2C`i;SvTw84JMM0D!F9rje)UK}!OUJr?E9`+dsE zet55ZfQs3LU@sVfL!^j%dBbu(6!Q#V4#*8BJJ~Zal`P`JQR2)0N}7TCjtaGc5>$^P zPL+frZYC8poH|SC5gpM6bGse?Q+f5k%Um_h7JG37;f$adL^UR2jNJ=E0|k*im_j zya@vUoNb5~1B=9`o2Uwh#AlkQU#t>uG*RU?iJyQJA}dmMDGW?ht6kz>Fi~laZk}}M zdo5RjwWOV1ZRv~Bs%+*%OSec)&+fGh1Y2Tu?VXs-t&Q1Or&qR->n*DGYc9iZ zAWB1fabGV}U@usYY_7E2@Mly%?0Tzgy`}phzmDnYI!w;pTW!Y=SyVrh^Z)RDK@fKH z2Mn>OuK{|9d6_$+bfUg4nz_<1wgf|jJvYD*Rq@$XNAbI)FHBr#g=OJ(6f@VkvQVtz z)2v>xu82JW$4wMggnx+AboMmH;BK$I54TsnXHgYt4hxzNQFAn`Id175AS0cceFRX% zeNeB=>L(ctBrAUnU~GPoE6o#3dpVjm3#QLDraPU-or>d-ZSt`)tm6Fm{L+g(2WQqi zcmG0ltB|fNtWsq_K?#;6WTelFrdJwwwmGI`*pIeFj>I=AW!=WEKq%W65R2S^+xMOjEm+!Kw%quSgH zyqVhYq_H4E7x4`&<3{5>LbsBF)g!kilx%!r+>x!VH*nn>|vjB{H!GbS-rz&}(3Obf(7_sQxG4GXqO8hq75>=&hK1c zIat8~5c{jPBqpa0+*6j`f_hk^h~4C~aFrrXBr}wAw;-#mU52G8zyC@KsVEE<&;aBi iPEoZ^MPE$T+p?7QD`dIy=yiOmyzG&#W9x5Zr2bz%bKheC diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvos.air b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvos.air index ca8eb58a5f0f081ae0c9d9479b025c54ea0e699b..efed062af38d5575437c9efa61c32a0314e59624 100644 GIT binary patch delta 48763 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 z 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

BU(Uc-ooT!XIDeQ))It_5ef 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 delta 48404 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 KV^b!5^8WxANdg4` diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvos.metallib b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvos.metallib index 9b6644279f66a7e99596abde3290be18f4814551..b1e402ead6628329b8b0e5aab0fbed48b203c8da 100644 GIT binary patch delta 2065 zcmX|C4Nwzj8vgc&-A!1MO$=cP3L664!dSZyn(CdlN#Fomn1XWDT5S`4+IXiJ(K_w) zylfUolm^Wi&PA`zto*3$O>eNApy!PjLa??4Me8~3oOaM!#@#w+ z_r;IfYbK-n3h#x6N8c{E`hCZDS6&IPTiR4txqc%BT!u({**FRgL>%R8q@Gqs)>>aj zm7|L?k*|;3u(qHh??D+vxigSTA`i+pqqA2Q6S!i1WY1HZP~q!EdETgwL<=c7yNSw- zY+pu39z*c9N=!(jB7SXFXTTN=*boR50)xOI(jbWOfQ=jupmsx~*O`w<5%0UdlHV?% z3Va}=3JK^a7Xc>iNAjQR|23l&sE{MO|9SmMln!`~XKmn}&pEjpF1Cv6a(6k{DyD0d z1PZtG@b3Ey{x^?*H6HYtEP&7*bnOS(l^gI;rrXU1MOs}kBg6oEtCTV!g1<7N!~O4D zsPw9uzuW38m+BrXA<4*TyhNVENK$T^C3Acy9xcEJ@nBH{coq~W=y$G8 zHy%YBbKdyViMv%3S;m`*P#MG35WGVpS2!~|CW&6HP zNm9uGs*~a~u!^;?3KH^?E_**iFF~87dUlD_;#7;Z)IpWM%&~zUJ@sfOB6}xD)@K6n z6aj!h0Fc41+meIhA{=rM04PYs`#)8RI6y%0|D0jxFA#)-4g~>#CmQ}FOHos)1pA_6 z#{!Mt*6*+b2Fld(0GNa!lj;);Rp2<~0x&C@|5mqds|(;FR2)QVKYb3~qt^q_>WAe( zr!2SMkgcl?hg3z?7x0RWRiH=wQ$Fu^^1=JXcE7}jbz&Lc-OQ^>s)1+e^Rwzv|F!`X z-G|6JA7a5tFY_DuPMj=JUCJ-MKCrsRU>?^_k-3>eY@AaD* z8Koo}RM#k}3DR|XG7ndTNk%R4(?TegAg%PEJshJadBcz}DX59!l-;0)w%DJt3r+|X zif>v-`iqqq9nV*v6xCLIns!(#=BDQqsaTQ98&0~^1ETtOKR*_uKgEtZucgbyuES$9 z%%d1hm3WT^_n6perOV0gbv#Y5*`78RVJ+}6#Ot%YNEKl+O@~i9*`Xg|pV9mHF28A4 zi}->60!;e@Lu{M%^de1L+6648b|(n0IOQ)Qt-@f!euDW=tWCY)_{;>|Q#|Wfra1Y} zk=>*zcbYrx%)RU2ulf0|SZ-n>?}3BgbPP_MC$0P(qq!|;?$m4My_&mWjg@{ck^T?w zHsEcJ%BO~rrJ2UMF!qu9p`y71U%TdER6FL^%kG;fyL{tB<|~M?*(`Gwo zlgmFWd-~4}=*t%im)BjI`eSYTihs{+>zgymFRUZ8kf?m?V=^nN!;!Vye0ZdMRhISa z$mrPOj5h#?l(xitL-2Dh!K;_2j-df!Xa>yM4guT(8jTKBA9C0Nc3pDRyi-P403OnX z=Sr)9q)i({3PQTSShq+wS%i`}ifF+(N=iIAE12wY8e|S44??MH0`3U_SRvscUOr?<%aos3Md{^4fTVm&y2{QJQR(o49?V47oSE!7 zmV5WsvL^bWU7Hv&dx9C*&u&z#-pbwvdAHq@VeNkzZ4Ulce=x(bW-IkC1PrQ-!Vz!U zDD4dQvP=~vO|+7uc#NB@uD1|VI>`ly4kpAhHI(G9CN0GCddU_2!f%ZFUBeDj$XA3% zSu}f7{0a9N^BGKtn$d+@dlfE;^{SFVOURx$@J+zQzm^>`M~RG>k9KE#>7pGXM&tML zD!iSgYz2P-blQ`pY}`jXG}8ckAz8}D^DsPFS`P_JmNMAAyi#F5gQqW?beW@bnz%0> zUFl?&CkK!Cx;bnvU_otMvgMGUmvmT+atQtH?n4S&!FH~U+Nju%+1S5;ley*1f7G5NsdHeU+AHn7Zzz*&Qzf8mA zcY!ZcZB~AK1J6c2l%tK*Pmpu6=s~mO7jNPQIXF$eS=bIUG9iBCRIjEA;7=QRsk67p z5$>{Ou_tb(0ayiHJ=S+x*3OU}diR!&6&-Ev)>m6oE$Y>>))n&kZ_;w)<}C81@pCgI GzVCm@dZ*6- delta 1409 zcmX|Ae@qi+7=FLI^h$B5N|r0F6`dPbz)p>rieQ{ma|6~Iu&P@xKXR3b5UAk=!OpcV_66$z!v5fHAMM?ID@;gWMA^+`JU%}-uI91 z&DVa(c;mb=wW4ZODFO`Q0b1rj9NbuUJoODsOb=B~SEkF2q)tRjo-TLnmkDWPYkLV)bC7 zU79?Axh`rnUzxIP!r<;~fS;GDG%wtUNn^9}dS-&sJC|1fdW+6bcIHybj0Us49G4KS z_>!|)0`>y|JJNt@z%>vW;xv#w0Xx+dz?xX4d0jpVtKtaR?v?a$oRgQcfx+tzo83ST@d6+JKBF(NT0HY!AU#zaSR98zlKQOlb_M;+&equop>1=5Q5 zIa&X>*qej6bMOVmIoWfIF&F3L&bbLc2C#+$cn1LV6Q=2x+Cf5`h&`8;C->dzoqOo- zs!=*-m%v$=Mw66GxJ1KtF%a_%uoen}$#(8$OeM?2RD?Y9-;fSSc!+CLVTCrH&^XO6 zmGZ0TKs>S-%@7Vv??!4+Pf==FluFr>3fd+CP^<$Ixym{{PRQDo*8mViW#23b=J7yk z;{QxnX$C^P=Fl1dYI{3=$_mnSEFjK0B3@vp)6eGu+`ity z{b>xVLTZW^<;N9MwbHxlWIr_@C54l(zlM5h2B_ar3c!!YW`}{`7c)$BwYv8TmFEqD ziSD%e#n(-Ax~)&79JzzSZ@vb~$}|Z>5eCm=KNR|8YFXZ(FzP!Vvl|}9Z2qH|9d8(9 z%~oblH`OfQp%I5+D#9RZ+0Y$3Xyd7bfyrrz6Jk;p!TtXh6_DC9aPPcR1GTOi2Ti(P<5;aNo;;AOpt1FW?T;1p$OG_^}B9@K_94cEtaJY xj3Z7R_=Vb&riEb>17OxT74NiYpGe6q(yO5%s!Ba`n_Qq4(kQv-$UTZU^e-2b%vb;b diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvossim.air b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvossim.air index 2161de5de98c2cc1ad6a547f2c41a82afaace2da..a49a9a7f9c6915b4e9902954969e86682b768644 100644 GIT binary patch delta 48764 zcma%k3tUs>`}kfq&ISyafH(%qxQHmIg8^Qax-k?~RJ^2k$p(mWQNv4V*4J$e6%BPd z^`K%+CACByfr)u(160(3L$T1vrlL_HVp(C4{h#;jYTv&9&(F`?Ip;a=<$0g`^Stj= zU*xXe(!wT-Em9kAM)td^mn^v!7 zt|@h0r{1)V%UpMi`EKN5MfW+m+rySRl5 ziuppc_@>|V+V)el#iQw!<;7ZU%k#9ELwvY=C4SKOEK`jXrthvxi(fpf60Sz(Kxr_M<1%q`uOrcjw zX5C8a>ru;rC9W*MM-hsKbg}3K zvONzorw>FfwX8uUh)7YWg;Ez|xcJ)M$0HZdnh0nStgteHoZ#|NW)s`3ekX0zl2S_- z>J5HT2rrguXcL#Xyk0z76?A6n5Vs-?&|Cd>GOL)rO2*tUNxlupqDR)E5GA8Sl(p169T>$SHjF4%voE*G&NF;c_-uLT4X#@WNi~1G zH&poy0~6okD8twX2fXQ)D*h>;y(WFuM1EO;Pm1DhU99>@u}{R`$@?}JhbLmm^7yn* zM1Af{HKCSR{Lh!lx;L2m9o7=-W0YKa zTnb%K$~Q^^Q~3=FPuU^A2v!!2>AUgKaB)_m>a=pO&M-icm|CRjB4w#7J^HzbfU_TrLWUUr0}gV8|(~D=&>O>fGkVp6i7=2=8cL}Zl07I!8jWuF?pt!ayl;;(MJ>^<&k`s z8G*G5@s7ADoW-gU5!}-u?3A}w`*Pa4w|=KS@_~O|Mxrv%tW3JeV2r5E5^GcTMfjZ# z8Nl+JA}+{!jbLmk&rOsl1KXz$NQ*-T&tJHloG)@R3e|yt=16Zqv->gwjk%dss2%WA zj8eZ9`r|n|(Rkm&-%17Tm-F=mjaK!C>qHX^kf{6gGR&6@FSPQ#oSUix(-GF2u9`Vf8GySxo`;|MgRz&sM2Tn zKPDPf{fFzN4Ck3vXTGk}JJ0kWrg)yL>QdJ4edV9rWwK~;JUslR(MlB|eb+_gaK?#g zy@0%e0_qv;Ph@d1^gob`EF2&f>PE;?vX5R}1TEDT0KCC1XT-mI6VTg9b;VzJ%##s2 zzNI0tj~(dCuaszY_guqhVt-au^t$~n!LloR#_WG***9la+(N8JUtQS0=B@9(R5JUG zEnB}Swno4;XgC*+tS<@>2N#688aP^mT|EiQ!6MNf?AjZMS zM#0F21i!i+3dyq=w}XNdG&gPKD7DYP?W(vNH%BrzgsYU{+qpsWcZP3R8RV|wdaYz$ zZ7xxAXL-KO`N*HSxspGnD&^Yc9TGq0rE%#uvgYo|o?DvzM(f*izxy>~_To3bew+Hd zKG#=?XdpXPVr81Q$6Ey%E8fmd$}U))Q>9i1xyxynZ}?M778I)6uCWqd>YG@Jg;8Ow zi6=|=;a|iSsyhK%ivgzqa51)!<9!s`_5##p7(iVhH8YpVCp zob56kK<5GUGk}bE8VtAqfZqVnhNr=Rivaio0G)Ug4EPcNF9FaC0GC(*Gyot}2&fui z49y#5N%DzT0TcnCG5{6gHrD_!1OOEPsKtQm02l^<^8j!W0>B7>SO6IDC>YQPfCK=v z;W03v2>_!3(22(Yz@S>6FxKFk@HO!Uyc!Fydhuup8vcQN_ld0l%LG`7X7_<~d13-I zyHmnv=~;ywxYV)=1#r2QE1#AhTd3dL$Z}Z&jcNN!__SrQg|wQhESK&G z0F(h>1ppcVAQ%LI0|0m%0IdKhz<>_{@Gby40HDKwj{xu=0QAW5AOKN(3XtOf(OqM? zn1%r0H~>xoKo0;8$b+H+0961m0U!+nPNasMe|;}8lU4Bg5rbdig59}-4g5LTywngQ z1zt^o-vDqON2KgD3XI#tA|d;l(#-fl7p!>`_6hH6d?0H8w%=;w1PAk%WJWt47-c3wy(Hqzs1M3Yw@hb(Y`@|}_ zPT<+!DDLf>e|k@TqquQu_OD*Bz*QNQnjL$%&+*ua#FYNVg8FT|=zNn-5@-|@`7gcn z6dq$mlcvt-)Q3hsbbev&97bFe5@)M&JV&VfM7tk}(mDazOLkBdUc&bycr~?k@B@ z_-Ou@eHT86uPYdPA$}o{mgD}xfVe{fRrj1nb|g9^VeUfK!M7I***|10>;OKDeY^oT z7Px!dQrE&FcGYSYAB>Ur-dwox~UH3=B z*XtVv)!W}a_%&PgXKBB6je_&k1HG4esJFbaJQyu+{-WNrSl(D3+AMEgQg7O+hmC;^ zwep{tjnPJa@9r&)T4NKv5sQX zA_2FK8(S{4vg@d!Wi@ChaJ{9LUHj(MR`%7d*Gyf;rbA4J`gTZWdI(u@v$KTkUSU>9 zLPDKs9A*vsCB>)iaoXMUg>3#J%rs8o;|-el!=vO^-u$(6`(NDB4SP!)X-}rQg4i!; z?JP!JC8#UZ=mRER^=-e;N z$7zYsvUyY~=SgQ%u(pnUvvU^a`G&@+vlnL;P;x&y?neQ=uW0vei}uJbO`R|=(}Z7n z1jJykJ7! zc{=roUMs`%^VIOZ2kTusB_DhDZI-~Iom;H3m#WOC8&7@7_Cm56<0pB4GdNz55#l#ir5=ci5M^aKo)*jGX#AY`Xi_y=9I3 z-rMgriZ5YfGwZ*MO@L7V@^||VTk77t_duiCDiZQ_rutg;j81GfDlKwsH5&!O6fmq8 zt%lWRJ*yhA_P(%duhk%NjcG;xA3oaq;gdUTmHXa)A2kXp)L5?FZ5lN*(5NlBK?fQ} zr%F#7*i1^)TIC-pc_3&y_4^y{vZWq-u@cl!J+F-EZ0drxOkq%u+WQY(ucXRb$ohD3 zo-q8};cn?TFpeOxS37&^0=w(RMNg+Qh2fPCy;tbH|JEgK4KixGw~aHRalLi5ajQ+> zCBPM2z*^w&64*(|XEKF;$M)oX`gvYOLS1*_(XaA^e!uHP=c)uk#;F8Vtmvh{y|>#N zOiLoHpnlu9T`ts^M_|mz^AEj`;`s!gLjV`|Cq2?36fTF|L9=4VzSQBalUsDSt9tvm zYTVT){v_^t`l0tJ=!!iB);#yF9%&@*yUtV$t<~Cj6<6<8G|xM6=kAI7ciD8dVctn# z+O~0RcuACz_7S~QGhuu%k?&E+(0xE7r=~=QHkZ`18J|Ako%$7M$~NpfMfEJgvxB}y z!ZN&4#OH*fkji3tlGI1YK1M9RpHRn_>LQj4gDk6`GSOJjR@d1H7BC{FZiRPc6`07f zdN%#+?)_&QokmC!S_bGys1r!P-?N+&n`-R7fYQb22^M*|NsBR327Ynk8#dk3z&mFf zK@Lci+lJ^$s7sT6|J!mQ2N3G|e7TUFz{r1=0JB%&+06#_l`-{LC=UAontIHn#z}xC z9n`HhE;>d!J@1zZZ~eOXEg|0~BH*taEE7nbcmme`MUPY-MKSE5yD;3!5zG*y+PrO? zgI?mwX`oZp|Dsb`Gsi~Bq+341$WaU*FgO$sYU{JIQI)UH2PtvGV^oKvd<0=i zQGKY7XR49^BrUtqOKu4Z!iXv%QB_qY@%0s!*LAfwbqR*T=C+32WmgXbUHiC=UFZ4j{%hbc`&4ZHhCQiM!Kk*< zc4!PX+v7!)t%rEX)C8YU|E=?_p@s!pd#LdX-~bUIg@fE#NcET4#HY&n*Uz?HH?*-; zfrfd;Mr}9fN)@FmNxuDr(baUHG>XG3n~meKP4iB{;RC2rlC+;NvYqY&5dHZv^!pey(SLYj7{)aDJL4=F!DjZ8tpNjRT5PFgefCuL7WQF7Dmz* zr?y{WdjOI(jX{mK%NoB8YI;=G1c5`JiYIM2{_}r=vBlw^Sd=9eT@Q|17o$qT)B&`q zi7DzpaInzG-#SeWk(eb$vy-O|CT_>%vBu3d4xPV*;~nr?4=v+co!0Y9Y@aS123g!( zrob8ot&fow=9&GMkNJMpIOaPUgWeIc3mExXBP<@)Mg_Ue#lC6fRU9Q*c4X*0Yqb6k zD)d-bRkQoRode&pr9lSX-9|n{oR93QIg3N(q$xSnxR2}(%L4XXfEN#~<-i`0?aJuM)Jrz`B&aqHlmK7tb6>~VSkQ}E{#{tiv0N!cSqc$2oljy^ zv#>zph}j{~#MU>B3N5c@!~T0u`hWN9f$tgxEqyBf`j#R}mrXY9W5}&kS=VnJP(=n* zSqE+vh+@+*kS>avU|5`m`qLOy+}MArJP36j#lL2+f2e#NtnKv7xivPkV+k+UnX2Wm zI7)i0$&o{PRrO=Bwn7fzytsU$7B4RFhut6kaA3fXAAk5`qoA!%#ff`P25CXuDw{!i zmGq6kI|u10%F79y{QUAiNUx?y54vR~y%TmodWci4q_--O=Tam8{?zRIUN)hw5fU|3 z9g4t z<35XltbvcMdr`elVB=Dw)&w>ZqDRUI1_~n|f)RX*jbLSS3kdZK1p|^j@Pf{=xAhX+-AlMPWv|IK?d-FlC*Q{|K8RO2h27WeV5W-HFqc=l#xVa1LW&H8;s2iODsJKbfAb^tQ;X z=HN$M1s>?ofW~!3^%Lmf^>6^yW7I3TPi_f?PT6I)5eE#qCq1nkAWX}j zHTMt$q7C5pqMrs4hvL8*>XUtgg<1m@UUX66MYH_Pk~Bc6U4e@!Nz99s?=R1ogyRT) z@KGu<_(e%Q7Vw0EbAWi=fG0anzuBec3~s)%&M2;&p1s>=mW2jqKjQAX38>Eq-ufS- zwi9PX!EH2hx zh^Hkuya*V_eEE!uc*Hyzh1%cK6EUW=(2O|Ixm83+EWHXxO`XjH_Vzy7=C)&Jam5w( zt|6~~HAG^`((m+cHVoMcb)c}D>ISw?l>BwD)K<@BbC?5TRV|Z1_tU%@opm2O>a#Pz zH>^&yxD2ar(DW0orLfR3m*m-Qjgs{VOr&^x#5f-OcZzj7(|3) z?pCKf3>7ZzdFQNbJ2MQD9jw(5FBH{h1~x7;Qc4X}#Y(NRD0P}ODr&#Xo*DHzn6y!G z*r3bp@xq#BNYYNunro&%v8=0S5MjnX{8&m=s4(r+|5|ZS<)X0bytolJ3E|RX@4Uz7 z7RmU91{M*{-6fM18r+C~A;G3Xi90cbTOkw3>-dxZzEWsl&l>Xjo*rw7Z0?KA${t)S z>}W2h#K*tK8uSGGu^CZseryK8@`-xgp5A9#!=WM=D(dw;krGS2=*}OTm62H9x|{1^ zDCjU)QbW12y?<_&4Bje-SHCv5USYQYQT|;>XGOmb&KK+)kSm~fGj?51qF*$3!2Cn8 z^jZjM5BETNT_!u5SZ#~XD;D;1h7ZS@Cw5$xv$sHuT2IBOlzEO#X9v8|Bj=Vq!5i^t*YV1ibRlHhf>IMr#MNZbriY@9nVT+ue z8s*LwW@|nbI&IC+ZW1C5^hRNRL~KA_H!!DA_yQfkD3^?hsp z8%9`zim(W3(bd+PiZ#>EY=+4EW)F^kt&6r<)vQ=m5{}p)GN9IM2}k8j&%H+e?SCl< z&x{blHuCay+|CUBCP^@{p4*!ts54L25nBxGTmL!(m;gr?Wu&!B_helHbotky{IEO* z)9A=1!vNxASiZuyVsGXv%ip^96;`i`5JuWlz|dyxRcf(aHgU6YI5SoS!*1Ln8A5!@ zt;mF}lBv4OfP`~^p9=&L$svj}S))X26t}h1m6O;o`ON_;DtVFgv9nktbXk-7XcQ4QtO-+B;nPFPnPv23Sk?@$6Fc#pEU z6|quz!3PHRway;ZP^u2H3QEFI9e=67+BvzU9f~ARdsM@ycTkB7uYTx#4i~&SBy~_Z zfB4Y*0)Fb1JoEr}@X-59{varTn+5clqNO(@&T8&t3LpU0PZSOkM%LJrbY-*YD%)OULAub$Yo=yNWv z4;S2QxyruPX&H*j59}j>H(|ANcV!9c(xx6=yp399ouxWghpTgnybc((RPI~xan`F0 zKVTF9`A{S{O<(hgY>vHu{Z$%=2`8Nm3dS0tqG(hn6uY)9`Y;+;onq`jioO_c*c&#jr0P0!z=>KtIWt(g;LxP9XP11S+ssHhD#d-;QqL%PVnk1Ju8 z5YkRC%^+dQC|oGI|7szz12oPJyFKf!i*4HXVpZi*Y+kHE8)aTX{XIW2nt_}5v((#_ zV~Ibx-C3sX%5lWsT*V^JPGt%q4}-$^G2esL7>p#JhkC(tv6Ii z!lH!SYB@a$g14$zY4c9Q6k=pR`ywr#=Cu=xbZ|9w&BDZkX`FkBt7b~TQ6DaZeOijm zuwin-NV_!E73M8*Pq3A~cyXtxAmSfB2Nw38QD)k90=8G$tdfTGHVRe2w6Z; zHh+(SEd?wDaQzo7W=0AB1&b*sjeOXC`G*QsNDjkF&#Ul4D`V-?_bBHQO9Hgnf;#@R zD0x76wt(&joUky$%7uJW`!&qMf8lvmlvNg+ofU&3GWcDrb+2#*cn-L>&y+wo?o6>M zr;M@|uL?O3!FY|GGL0A;P?4?QBbiSu4>+GqBMt02SUv}?H@mPmf2IDyi@ONs4_6>w zF@HZtdDi?p1;G4K9*c@Qp>hhDJuZ!ks^qJN-oIIVoqerGflKAWVIF7W9>uC8V|AbP zP7+25gH{EYveQWA8^oFbbG91Lp^rJSNNYh0PyWhcODC@yMllA*9!_%55eqS9U#iaO zq(7yTHN)h%h$jIP2DF(Pp;1}{>cNn$Ur?nQ*m<`W0xcU|jR1za;eC63&>9R?d0A_w z)AW~_bWjWi!Ib~$91St2oHJ^5P*DbITj(-#I zUxrz0q5tp^`s-}YlUcY*<*dTK!*NYl0%hdGh4NRZj)hX~T>&z=>l!;bpEwzCelcyo zWGV4kfDy1R0IW@m1@vl)m)LIE_lX;>Q#J`lQvclSy@WS=Epqyba~06R0Lq~$ac<^t7Nx zZGFKO?sfL%PHa!NUNG`|7I*Il%QE)FBHD-8vdA3iX1ul-+%e|Wf%+g>#sY|(GAs_7#6=dh|efYsc>u$QsY&<_o3iS~e9OJpCS zDa7{y+9hhhR0SAoDU2NmTS@i{g)qzy1wmY0w9T(DvL{gMp3-0x=$4p1RK5oR%AeIc z4RD1eQa$9abScJNI7XckhbRQu(@fRrOb5P8e0Y;~T4Ytz*rABs3=d9UsTS?sMctDvE zZ^%Ps!J0u)l?9Jm)%DmT0QkJ66mA9gUV25xsC^89=QU%RB8PKGSxkiS)H#qhfqd@D z5vU*?pz;E1QV=VbR*425j)kn?&SsnyT)!3CMVt^9s`Ar>A>(nVkq|ZmKNbjusg*X* zJ@xS+B|c;&H#HRsr!+x@*|Ik0B~NbYFyi3*A4{Ix0!zMwOKruMny8Z(!QE`?W+b+w za7A~GoN^fFE<~4G_YzTXrs;^mawV4VDq7gmeMBx?15uCqbw&YBOYh+$Hrj##$}t_N~CGner6EqjUKZy!-k zHstbW7VwOIWkj6oQOOiTt_&XA;Bh28PBr9e;jt4QUxUYKhFr6J=1N{Kbe8QtB1vx) z7;|Yym8q7iWJ;r;A(wv?<`Q%_Qn=q7v4&T%tBe@tdQ_Qi$Q8^k;Hhu3=^qjWyyDyJ z9p7O*m+$i_cQV-*JA1f3@yMkB^aH>82)_Y$RdtHRlXn`uiT7_Z>!(-my;|!f@0EFL#msJ*H%#^! z-{Uhe^EW`k+z){GhUUH+fbh|~f2Q^N5k53;T?RFTx!;cg`i-Mu^d8q@n5Y)F@HZ( zc(lvpcqyNvK%LU#wZlvu3S0~K@VVd5PN>jwX1Ai>+7OR*GVk>U_jNSf6C`5NK}h*G;{(JXUC#Yn48kGH*GMw43JL zF80O4A@{h0yw$iJ0h6uMGGUf(>bXVuWCzsuyr3H?mngp3d`J z={{MrD&W0*)nr)zpX*eIC%?C9e_-~pX8i@ zVpwNI=k=4R%ZEK_T((P`sw&4VAK&}2{qnVoM)u9tYZu#U?_QhWb~#S-ThnvT=bK_O zWxJACg&X8$63y>7K9l`0!bMCkAxiov_^b!tv1oGzJTd%6t##XzREaIp4TnQ*{h_w%+EEW{A?vKkXunenK6~A_)SUKG%gysKo1re@sYpzE> zXquXXJbSM=CO2q)Z(fS(G>dKuy=V~WB&B+=|@S&q(Y=(oosgJ$CGC#YRB;;a1uEy*)DYZ>>q zySTiJWWX>s!YMUBeYF{tYu^5PBx=)~{CYAnY5w?nKiuzWofcreGMyh%G>+KZlOO(Bq1-uh-8Z1 z%u)J=%UiiY7Nm2{p~`p%wM$li=a@s_BXe{zvABM=r(|A3iq<|vyDTiDpacetqsWje z4nrfZ$o_LYrh&8IyJ9qh;+-TRcI6ITVp^`ZJ%B3I3?mAW;<&)}|@_fT=$CRc}iGy6yl7cIaPQ!f`eDXs< z09&z&JyXF`vC*ZiT)qXS$-EYg-?!s=i$6PyzEE;rwz@vp zj*!#qW13apu7D*y`Zi{MPx8(ZyQI-V4MFri#!Gx@8lM~I5LI>EE9x>3Bp@6u1PPd= zGKAX)W{%RPyuQ^wm{zmmULgE_ZtJ~pulU%3wuLA2jG8a+_3ba+#=YrS)nwH_{mowwDcb>TE17a&U(ercAh^bv*{6xAa>Zni8)OnE{SfwWOFG zG9cCrGcV>D-e(zt>iXy}acd8y(_nC$Wop%&G^a=X62jmH0g-JQE0Ht6Mr~KpUS_ep zvXGMnP-tTAM}vJ@cpnUV7g)WLcp9Ho*uyl^IK*N2-KVo8r`&feQKqT5AMQ zpuOTmP<1dOik#5aYmy#Dc|41%SsC@Y^nM9Vty%Z*4MtDX(_YOt4~H>|=6YV!c(#Yn z(TCML*f};-#xMRmNb0A*>;TGU$kG5<$Ek2KM2kK%#;{PJolWh6h}jMkjkalu+e5vj z`<&G^Uvsv7v)nY+P%Oy+`$7D6IJnj}3t~R?m2upi8FGx_;91W^#pTH2ziq8o5EY?z z`YkaJaUlwwR^B~?@d_pQQCEVbgE?Wb4V+RJjp5N)pXyKsx6QeWS<~k+53GFj;{ep8 zneaFPEzmVV&8#NnC|-wy^WpYeSF zYSp~={eU>hHU<~OdkLq=$mhV+&%MePw(m%p+zT z5@ag0R1{&X)x7az0P57N`7umhG|6cO!4N}`bO7Tw(CiMwU|Am^g4GvFypDttPBy9B zM_XHvq;@Zq8)N+@2sVn1wcNz!U>oMiyy7h!S(>x>pyP}V_r#tozT$6g?M@GW@Uswc zFfAd+Q4Rm60FP^zNSq6ZNOelb*C_>VKLY*SQTBkZ4K)vl33ciO$k#&jd95 z<6l}7R+=~(ZS5Nmwom6cnUpfk)XsU_qd+RZ2=HREan(-ht=8x}gXQCV>~vhB3qwP) z`A+?hZFX=4?YHocPblAVbTLP`5*vDM#IPj;96`gEWRI;Q#iDHmccZg+oFq)F(GLW?z--Kl`VsqSRYV<~ZV z3$E1ApA{g!X7#f;@48nE&oa%KX9JN^^X;<$o@75`6+4OJ&NbszGGUdbG7wWk$1x4( z=ZnZxb>rs^$OWRq+0WGj&B+o6wao(z1xaGo%b4pNmXr4WrSbT6GE!^i{5oc^>S@o^ z9%BrA^)v<1U-WEyN~0Zi^B3$Za6Rl+DzFD8b(#ym_J@%0n_qdbwBLS>^cv$#j$Tdl zZ$nA{{~#Yt{%<^_tJ?V6al{b>#Bfx7=!e4-Inlij*=ZU{PoP(Nz_!2`!K6@zp>|x8 zR+HBgID%PPS>b55D55kgeY18J5#X|?#*H2KEI4MQrFp}_#r&LledJ~i!{hIfD$V(x zz=5WKoB;c9w8e$@i)Ditf0t$pw#l4Y_V$d5K+9tD>>&C_ZX3f2t2w<{d(gx}$K-c4 z^Ir(PH+|toUv6)!(j0iv4?WkMei1*2b0ALdw509`?l#s0Y4`TI#1Xd%_8@+wrq3U- zoWwz;0~Hh4+bofNT)JlFAA|jx;#y;S6QQ)bN7BDg==nMOS5BEm{l{q3qPhOZ2&~oD zPIPNnf6krFnCH1TK0T%-PkbDGQ^8#tuf45ZHk*BhXM$sCQN#kzocOi-JtZfIK)7EX zue&Xi%8na0qB*Mho_X=D9CMy7qvp1DHhsQl0^WQqll(UA4os|MLVUi-x@vSS>m_XJ!9{M@WaolN+wHq9BY#NH^IM3r>fHkZRfjP{#X+OXe&pcoX z<|!iO6il%aPq8*$ye)r%&)Xkx{ zYZCvCK|fTj{(Bbc+cNb@@3m7#30%$YC#h@OwcXz$t-K!WF#Nz7{}UK0_AR|JhO5D0 zZ?eij+0IE+qWNh@vLew1%+4n2nR&M(S?9xz2EDdMb`_K446a}MxQ(2W%iKb}BTVcL zP84iZuXPx|q7=o}kmzGERyFM-FE!Qi`bBS8pfByHGFB%dHtT+wiRBK^ysqgHX?T2pAb zo>>_cM)PY*cj2qa-L7apk@RBMv=_(7-&|3$Gd9X^02^`&f%YLi*+HOzVTwUci!qcj zxO!zzSP;XJvzJRq0TcC;U)#@!G~(zuXb6?A<@^$Xa%5@Pj@szG1-eu8HecZun%y1r zHErgUgoNwtX0Tj&fKe{ksC({|Z>{wyt+lzc9mKmc%3Ka5av+2GBc*bf#mQqIUpeFqDPy62LUk18a!Z|K&~rZJb==y}2nS-% zC+WN#Pd((PEVLNalYQJ!!XPWRnr+|_Q|(i`+DW||3hDbuI<{Rpb`__6ru8Zv`$$Uu=7y#@+a{&eHkqxqi8I1tn-u@B zO$xA0_pvd;wy6zllm51TwrU>Orq3zc1g7fDv@cMFi`;6Klul-27pS3X4!sAp>SL4M;2BrYcty^xS7dJ2xRhTWLu8_eH~)I1eclm6MbEqVbFizlgphclAXvR60Ul?TfmuEt*BGji+(LQOAS;1p@alTv>a>Q(BTq$tQ$ z{%D1~z>!o+os-HRd}TVwM{KsK%Ht zu#subS^Nk~7O(Oxcgo^N*|PW&Cz6gK!vav&LYl}9L(IymgBaqh7pmAUlWOV>js%uL80?0ELZs$2r0^PZV6? zGY#v&ov$)6q5fk}Hq><=Du9gEQZcxlu{@CBJaRS<%|?sJ6Fijje-!0nTmk;Dqu>7$ z<#~Z3~}Q&DsEgEFJ5gkinclWI?a6o8%66Jl(DzYk(`3p zaE`}64{MkZ3UjpHrrpPD^TBraB*DUQ;~s2x--#Emoda{!CxPLuJdSE%9d!|SjyF6x z*a2nrfjK(!6y30H#Rou-tU2@|pht3^(;V}{9CL&O+U6i5L(ydg3LMeH#}Fy>u;N9t2_-Si9mPTBhCz1U+};gD_d{#=g=2;f@i^b!@<_u z;yEg7R#7K7sw|5o#+}Y6y+xa<`tYv6lm{f3w2W(v7kn-IIDx$ij+vNe1{N;xOz=C$ zgOuU8;RNwH=^u_hL90o9IN1Ni> zGxjn3Qu#J<^*-xHG9u8R;g1(AcH4KqI3yI(JY_DVd9?!*IcziW27uRJ(|fNt6zIoJ zo50?ufeE+=$a&DoJ50lT*HPS%km?9DfJ47_zJ9X9Icmv^5h%i|JJ!j5O30rhP=S|r zic`HvOBM`5le`oooZ2bLhCxV(ej$4Yp>Ss3lPPCnl^*1vNR)x5lA9yZK%(Z*x7Uba zWOXE(%{eDjZp;OvWvSr5qbGTT(RidKWrNXtIruoMyi&o^1s?w*$T1F<4$q<2V_n3- z((^n;56ENzmeqxx9Gv5%0Rx+lqX!_vDt8d>b@Af&LADoRy*fn{gV1m3>bXrrU&|&x z>wllee*jVcH8bmLTA!~$H<*SRBDsbrme)}F*APFlHeOs0(SAv7pOYx`q+w5nzso9^ z<^iWHzn~~zQJk;)n7irW96DoxYN*>NMlO$&#|!G<@qd7En;uwaI_QrF#bcghJ&v=E zxw3P-;HbemY-rrJH$6E8kZ6>zfMEy0xQ~jrDyFK!7SNMm=;URL)x01JzN1>Ps!=GD z<{>EJmDuBd#U%AnD5#GmC$d=B4MG?3A7`FLp-7bOWM*xVp28aRc3Ts2^*@5%0!uB50&-ONP#a3ePzAoVTkX8?aNJ3Rv$4x2q#@o zlO=tIBe~pZU50xW_rZV5;y%RIVE&)$dZp0d6wEuX%WLgY2{UH;PJMl`WEHNSANCz^ zz&w6yd1Oq?K8}6)O(aKvf>8`95TK(V*sTH-;zIw3{8NCUBc1CMlS5rCa$_k~Ol;#@ z-r_Zr(_+w4w2M3+gSI0znHq~k$Ve9B%U@)DEZUDcNSP3k$V~PK(Q7D@jE{p$1GzE| zB|=nxIt~fI=su1^r;&^-i$||PL5;i{kH(^7WIzJSMFpfP0h)G@m+?hRx{W}|&~f?* z6as^;8iC^BDP$z_C3Pbq=#EA*%9F%do;k1JxYFwIx!5?X zkE4^s=ioLh;eb;!DlPyU4$a2cj5yWKL=7i7Nz{O^kWul2i=`bN4%>QY<`C#6a)k&L zp$+73B2b$(WbkWf7@AH_eGLtqU}2fnXk{=8ctK7^%W5E){q(t3tMRDYWu)h}6`q`x z*uioDzAIl*nor&z0%p=V$>5AZ$~T+@8Ceqs{eLNF-^7zdHY8_|h6-%N!K;D78g{OR zjo3}nok&zixwF$`*BI1q!hyTR#O8_qjKQ0mi)f_-vCARBcSQF8rgUm%y?!k?Hr35a zPxpa#Uo=u1n6WLxtry-}3YQA{Idbw?6pNmb>&K!nbeB9d7L6Khp-cl6;Y`l3#w3Hc zS%U~?L!N_aVzGnlEdKqxe^MjGV!_PLLbZ1sDsUDmiIqM!1LmkGT7;^haibRKkYwYz zVIyr0_2^6`>aB%ndPn25t%)=Chc+Jh>z>_L%- zC8%)H|4B?#SBpHtex;zXcbH=0T&O&CeCL>y_66&p9vcPJ&&RZ>4 z`(i0pyhI#w7R1-!x{xOrufQ$#M{AiVa&UW)qn)0YyZ<@brps=&E@?>>5~<70!fLH87lRsm6?m#4oC%r%r<_Ah zqF4x>!aU4zBOBty*Pz7l-y-om83lROpDMflqzxDA%nPhw9PMCvD^{%8|?Sw$}~J3Q{r!`EioRbt{HCR7y`~PeG~i=x+wMRq1r*A#D)yTI0N> zte7OYQoxkF@5r(^lRz$xfaN-v9REf1*}VFp7uBxxzki&X4ohLCk^8| z^#^qhX}B%3jf=Ze0UM20Pf5cZEDZxeo2^_YFQy?i$|9#tM}2+yQ{CgmA^gAl(95Yk z+&Zs&G)A#aehhrtQlv5j*l0SjbVJW`MwK`bcm%id&$NBSG&Kw`BNc zE!=2KSw6DSUFNC<$%+vej>c9k}1yqMtfg&G%iE`@9rCF#P$ydaBKXNK% zvRzlPt2vzw#qj@1Ku-0kaL{jx0k(oPGsu)CfFm={?Bz%`{alN5j6oR;GV==VtsTIjg&TUl}xCZ-C4Q4m#IR|Qdmj^?obq>%Z^>HfxhkZ$8cCDj7XtQ zh;Hg%l1lRlX+9XRyms146>e)g8^Vn6ElC#I6aYEc#B?+|Z{#i6bvRMPTN|cFJblMU|Rz zk$jSl#`&n%htaY;#r%?ZcAel#chp%8Id(2u@MF2)0RU z8lrdEi{Oa)%-0yxD$&8gq05t@T2SLv0{cUDCKZUu7xU0Kxhmalw!LUJQY8XkYddyO zx?9^fr&259dpY(-)zXvB2Un`$q>qhViYR6D>O5lg+CJZEWwcm(S=(@uaau8z|1v%D z!S+{})H$iZBj9tvWZCMxjcar}x?ky~Bu(>CUt}U5&xcGWhjd?nc%zwXuWowZ?lzjl z3kydPZ?di}Qit@ouGMRU!FE`a1OBFUZY9pfXW;_GMJjU50+j4RA5QA=;{o!{0yK8A z945ngSF&<|ohE!B zV9&we@VR8>8%V&wyd%fWMFHfgH_&(pusX=!-#`QUTX~0nb|y((#_a*#nJW_5??Vxk zHj|89h=zNOel?hRYa#MSB68zG2)e`099xJ=P|(su@5c_cRHY=^T1TsEBCww;JV=NMfkuPTPgp-RtIR@X(NICTtXTXzrh50@0j zQ9g;+@6O1zllNqZH9>VYmL>43v`eVDsxP?kxUVI9K>O&933hd(S7EeWI1rpQ zGBP6zNsxd%oP`8l_rhP<6}g{wWzI{`8dmNwqU=a%DZs>7ae9%8f(r+d5>yp*e z22%&0`1{G)MQC6&IF`xHOl1X8w9oQCvt;mTG!3lHmoYvX|EDSa;4ecQuuI%WP5Ne| zg~Y~jp60l< zS~9OQ#e9&9Q(fyJiZUfRcrl7jRzx74ED%ltPZqORvupWoi=Z5C8%!7R5zirIv_Xa- z6_4={HY6)hf0>v7DbqE;-l8P9j4WG>=Aqxn-xtG9R1fgnOu$O#ZydvFBF8TQJyMfP zmY{WFVhk*af80}L`9926Xm_OY0m=!JdwTt$S0 zXAX2GjDdWb0|l_R$pN`2Vp!`$6LXE}M8NEju^oklh~*`B=)ptNI3RsIraH5 zxiS}p4N*^$xu;-~D%i8wwFkKlEXuC+*V<1c!x>}p%Ul#N#lUj?LhVr_c0VKYh;aN| zL*iBGV?solQxGnT@+W3Eh?C2YLPPm|)J|?yM-I$GVkn9(&O>u4A6Ve}8ED*NJ~p5- zrt(9IE7*W`CKiz2;jt3fD5J>sVTX#m?Bz&OQD(EVHD6@21AclsqaKFj<4K98~PRWul>@qt&tcuK{e zzls8f2k(Ny+LVYVkzc2av*qK5RMsmcU!TbE);LTql9d=`&c;867?P`+8%@Yf?$G+D z*BR~PIhk#=q|GQeg2NV}IFDS zo9r6j!gvL&82Z%W@Fh~6p|7p~7GLX;ER*AvxyTC|Vp}d6o%Er{V_sXW1f;Tc1RD~Q zhl5cxs&%k+AUGyePw;gGXt==3Y6>@f+=bP7$kY4MbH={ba>duyPvB+e%s4d<^$9)q zJ^xWn9+t@}uyN#1(zX|M?*d?f-NrnKOY_hyPr(#)7~d=fGQ<<f?c)Ny3}u~S?D#%QWb#`VjPQYJYAS3E=fhg&YI z@OR752;`3kzJ_94izhqr(9ctF&TB|T317iQuc1Ug*^%!Zrse;nmR>i08{J&Eu1)N) zyDV}wJ&KL)M3~;iH(o<8Kn6eNb@U=SfY-eai}n$G`*pOGnCH{;Q5>T2=6n>2lJW6; zh$fxH0n1@IxrAR{j#AMDd}uiui>kM-K+y$|tyNzY1VhSsGneSQqf&ndzWyQMOku!G z2-zsXy!ZY=rM?Tk{x@pDWlQ>1!F=%beF+}Ku}+*)2Vjjv$;enXe%dy%y}n}~`U#x0 zU9Qw{nM=ORkPpG2-MP&R=oW|&rL7S>uh-8KR?gB7au__=`@Ty304{yfxBH?c_o`qy zTsp#aA~<~O-iW^!tGN$rT-)jZxEzYD%)=ckP^gp)(;MtCt;C8q(3*c5pENf&x&uZvdQBR_#B+MzCjvuT<11C3Z z=g-sgfyXxzEdo5&W|mjVV)BYh=oTMoRC|4)jl~h*9>rVX7V#JFMx65|ign3&zL3Wj zzWvCT@{wBaQ^!U#=0g$7|>Q#^oZosowfiEx`SFC~#KMVi13Qb01@c07g_Xhl7 z0a}j!z`ite`%O5F2HeZDcnytmPzLUz(RlO-KchoQq^HZT3Y5^jG8~{s-%_b>L$@=N z9vyL2kO1dP3GB1DR@_X`5OJhOH^ypC1H$5Qy>1`cOnS5@>Cww%hGo#B_vM-v&0EsKk|sam~2 zuur#Fd|dS?2YNFbRb`@|UJ&q!i@B*PSs>q6zaO&K-8rp$P>*ozLdnV4w$}9v)ca7a zxZngt>@0g=L9hi}NhaLntGJq;?7t5}LKf9yNR0o((-hGV^E+27zQH_=?PfimrVyfl z+|$6#*n9POngWOd11*AioTqOFZO-OsO}?#9wGHp9vp9hIBK1E$4Rg=fL~DJfFcwqZ`j*iIM{Az8t_X+ zXn?DQ4?2a6%f=$K$fbQ8&n`3IUd4c#%I%4+$|^=VuKM%*IbaxWFNQ?=1ngP@!TZ{s z?PsWPoLB;5(u6maKq4m@SC{ZWR#(ZNx^dWAq<68D^B|9Ad~7WWbW!kOkVf3K7OBw+ z?6wY2Y7!i`4kmzHylfpz0L}RDIuwbn;P2O=7<3Q!dkc+2H*wBe00Vi9jc=jRaO>~h zf@dqms`Y>r8G|#|qgVpiVn_J6JS+TZbMj2#92l;rmQ*dEt06@Zr03SVwMs7LeNUzS zkxUQ-w9TzcS{E`w92GYog$ZIQ!6&xIYQBL9qP>BeAd>oXvxE0Qx0d<2%*6{}&0Cv3 zLC@WC&3NlWc5y0AdQ2gbI&n|`PxJSP2+ue?x3WeR4313KNEgC=oF_5WL-YypF;{SQdl|8t;h zIC+~4P__WtpY%iy#KSPGtVmggVMUO#&|?^al@%yU2v%00>>mV-%K>FIBw%jExTM+c zu`r>T2gWV!10BYCi^HB)G%nnuh4e~t58VDXD&=!A>N$Xm+R7NfYs*luV)PtIuIgle z2fxQJNSX_*AvKd8pD9Cw64OT&_VRaK9aSV>Y7ZCOYBKc>0=zd+gwsu0jnsj>VP}q@ zG42H)hEnk~1}6-cY(b+Px?Erj5qxnAn#H#Zt2j5h$^5u&m(3?YX+9Eefkz#h5t zNP_;T1*^NnAK;n*A#Dh}mScKZE&LuAVSsb}3-8BhBLCZHIh4oKP%D~Ch8{fsZ=gih zq~)C5t!E|(4<>~Um0fqMN=>#QJyysob8_hguJme{FD-h_i)`u|FMkIGLgVgu2TkO&tE`P{;UF{uEAF7Y zCxj!HpS+8ni-)%2 zuuDu^ao8o&Rvf-U7*Kb4>&`?%t%UDx(x!gMFe|bCJv57t%CmNSH3wz1f1Cd`DsOl> z#I+vcS>-6)Gx$ARlJ7Vn2}((CaaRj_ug&c&flyW@ z1e$f<94vnBlE~L-eJ_zBPF%kFqB4 zkX+WcE#TUxH8E5)gYozP4?sM=sSu7~JU%%~_=VRluD7x1;A9nEv;#fk(LQ0Oyg@ut zErn1)DM_EK@W3Z_prJC~l~4TQZ}W(jVU5KTZZ+l#-!82WYse6NxXgeA z7nF)C17iy_gnp*AXj@>Ky;V|;C{gxnvKMl2S|39$V!+r4sl5UCs1ZewQN==^&Zee- zlbBEj&7x;{dwLJjP!fJaHLI&2vQLZMfyW_^gNAC0dc-sl@`NTjB(0E>D)KcTL7)+;1R&5Dszg3Vm1yLs z5|+8A%*1)wTf=ZaO% z7f_C|_ig0}cGl)e93q}XMa5-U2e%|%RDWAi8%?-_kaucD>0 zTQRA7Wc3hql7gep`uWoVY5?nzU@}aKUWHoeTxB6XyAzqon_!_bXI~&)mZLLngr*d- zP?;w8OE#zsP3$=qD&su~R@97rXfg3I2{nL$qO~oaL2R})|N z@@C^S2}d+D_4*mYN)rR`8WCO&)m|S|<##crLMHb1m z4KkfZT!T}0qj=Z4PcqaxP~Kn=>s9_3?}kP14{Y9z6ulo80~bmQr8@=23hAlXEPwS}rQk>(~cb5~2D3a4nB|0WTcl2kh z`2Zh6~2tn`aC2EfCSU+s} zHa9&B#_^Xu8kt~+0<__d50G!NliQ0aYt%6lv$m=)&_d7Ghh$9YASLmr55fI0;MYGyh5Yx1xc3H@4I@j7-h_rFp6v+w3FcOfMbMK^DD`ea7cUQfDK%6?k_q{VAKN-`v@H9%EQ?o0pu_>eoK_(w7h7R%~JVN{bCFFrkvgKrDkdiZ9ht%dOBT^ z9B^;HUAcWSHfF=RSEYEEwk%e%pj1YX40JiwkP*DNe_)9)nU z%v+5s3*T*~SgQiU#G6SMooDoOQ%%WaPnx=1;|+N7UX)6q!T5u{C@fSL@1UqAiZRur zS|L${zJ#=m6%=r5yiY5nZ8_l+6aI5A@^{mI6;vN)*Fme);mCa`gz|YC&)f$vq`{tT zr;b;i4y%vNOz#%KVn1)vADMk?MBBuT0s)ycr*94+7`;mFE}6*xn<8YZ&j zNo5Rm3RdjJGCAQiO@sL{Zyzdz`B6`J=I3KItzh)z6BmVro4G^~L$HATLsXgW7GXRF zu6seYaj8rW;X3co7YsSMl?&)9Kn&%s-8mU}TI0zOyQ@;)1Iv5ijqIy}JaBDIeE`EY zz34N;Du~mWib$N!KaO{u8s2qG8+J~Hd;;^6p%w^w=rvvtF)X*BdcRy@n1wvr$ zF86WEe)L}7^9zTeO99ft^?=IW>U81aoPs>tjKph1(qv=MLo1R}I``1enM zftQP?*MK3u3+rl7KXecq$wve}Q3G_I1l(PNo_Psre#LJ;nl*1AhJs!Bc zFh(YcwYe2@h)>M|Utd-`2LGK;@G06W?ihOfCQ#ph{uE6N<5``=NFoFyn6z1Ul<HdU=!SnUEPE(Gv;l}k{8CABP7lJ*Cg0Ms97(L#u;$A1lNKU{+*Odx}cv$ z*Q_l0%Y=MIqE-j*M$NGMg5ELmzcPeL5{a=_ya6q5M^W?DPa4Fl<#J75eBdw|(&q>* z^fHanAS9{~06fP^6Mb?RIzqt%#uTcVYu7mGDBtnT?Z+lNt<5Q zFqp&!6+}uFDz7tmjC7LynqD-?z=FKN3>FAf@a{ZBTC8_M1OFEqA0O1ANqjHV+C!{f z=>M-8ADfP$X#;p80}&A%SSmc}R7PBaOt90P2^GBxe;a}Z@@Gn%eH`BVzwqYc=qvs# zX>GfwXGtr4&rX6_V`5;%gs<<}!x~u}+m_mC1%wl8hh#x2Sll&}Yc z$&R(-SIiI>KaV$>0f+S%o7kfZW;EDqg!^h6XyZb!7(1@?mI-^+qd{JC-Hp5gLwN<% zIExf`#r-p0flOWj1KtA#3YNOJ@e0i66%d5h`?R)Qe9np+8KY`_HjBN4JHTn?r-DR}?HW2f|0JXuOt|PI zgd!U82PaWK%B38ihELRvZ=VGB_zmpTfChV?+AFHF0goP0d%zR1q7Yl~Q(gUSY7u7K)rdR+#pG}b zx^8Kb?>xbDdl*zbNg=)5~3a|=n0lC z)$p7BAf6J@PuS|@oqltkB?|yo%~^s(UGhTu+;ORwT)SMV8JKI>VA5@Zu!(vT>J+)u z_{McbLOkkrq@diai#shI4hSyuFfrWhlK5KZS-9DIO#*5ug@-huEJ$O$+XT*L6K-rm zzTxx^&2am`4BEjPn0=h@2=$XCJe=uB?9mLd&GUFlGg61YC3hY@$7=9&I{d+Ftj-F0 zWJK#4-kV9smCfi5B}~QZKL=z+IX?F}WPV+naOdZ!|0pn!xj26X3C?q5-BiLs@4ig* z0nU~>gOSP+_g%0A$ZMx#HO*kK8dC_O-giR4Og#P!3ITfc>t|4r>?RnKL{;iU^zakO z*!e7)^go#`Az;Iho&PN_)f!AT1MG(3rGhR7H!*UDj6riNki<#eA*7dJ2(2?Kx=`Ws zGye80@`@6a(l#-s6&1sI-7-HQyHhGrE7AaetpZ3T$7ufOq~tUP3;Aca19dTi_o>86qVm;oeVd(7ej}^0Y7PlV1L2iFFnlqq+rxXI7lu zeB;18-jONfx=p9L677j$nHwIUTvjUH>t~x|#;9s>(0SxNyCw>NjH5K&O<@g{qFTpl zK&_Vv0WZh78uKT*d?0^xGQ2A({8rMd0BRCEL5cQ;Z5YsLa7OXnCkJ;?gS$?HlVV9M zNwQa8gm;}se$e4hpGOhi4@2t1?Tw%b;Wfft?EVEbWCpmG_7Wz3iJ$N^QsP3WHql-} z9m>1L?|3Ja@X>~&VfGRRehIY)zWW6{j~2VM0^3B3gIb{@_~Wsy$h!|6h0dQ8K2aQ` z2wadA@|b7BZ$Oa%c{ET1;ItJXZH}&{JvyBUqS#up6`yED5rbePatRAYuaVQNnLtKj zCqt1st0N<^X}?%fEAD|O)DRV}r5BK|9K6B8p|#O{km$6Q2rT2>7tl)a$nMo&JSf2f zzeH2`wBmZsEwJWw{v)k;IX?0w3W{hqJhSup^)02zleN? zX4!{JMqI9YJBXoL9Cd*4R*Z?r)YL-!AGKF~9#*f*N_V`mNgHbKeIEGPODMwY;8+@9 z!z`|7%;1u-wl&|7jJI5Z7uun(H3%y?9nIsyFE~8NY&Ak3L#26(;Pf!@Z zN53b~Z&n0k0)GB7bmYPcV16M0P15)r}#-OZclRD2}Szg*~pqqA>wazKY7ghyMO5ngxS0r469Q_!^So zk~WA%)nSKg*7N?j_!^{Dj$Q{g&|lZU)jo;?ucJ)BfmDBm0`cDK$OWy#pIt}m;V)yq zvi>C+ZN`?bP%-Mj+3jcm+;LMo+M+a!fne(6Q;I@Oh1?EwFBOO3XTF9dX9>>!8dlUF zap~6(&T7YJzlN#}_@}P{*mV*Q_y+ZJB#7P5w7GcPH)!pDzMSG)=5pF^najQLEpxe@ z-~Rn_e|(EZ_i61%1kS&RRO_bOUh1ACJk^5KBrxMQ^b`E11&vhQV9L2hi5#PwyF@Lv z!4p!4zp|hZQg-vuI^3%RGRhqu!*BWHDII7ktTX#Mz_x6}Uv{8Dj)b0O7`p_EZ=le= zYz^x`4;0i(%-%`&EjZ-{(kM&5=w2TPJ7b*eFDX<%c9ZNvOXN5Fhu=xY6*thUl*^mg z?IsEh9^tMAeQR&M8wpmkCh~!h0q$Vyye^lUtqVv-Tn2vmCJL93`)kDRGx57O5eBy) z`W6~C8^B~oYl!5GKm`A!gGQFX*E7p)WU6$D{HQ+ks4zUs4Mj-68u*DTnCiJi4g&CO zEsZD929>WDtPhklfJkVI?>(1&>jOm%X-R?>-^XY0`CI5($WI<4vYZ6)U7qw3oy;~% z&EBgYH@p6M=?nO5-1NBd3;6OQ$#2d0!fo^xnuKTGVV3DtcL08G!Tau@Jdk5je+NtW zC7kpf(m?>U@jDcb_T!(wL($%CsKYt;l11EJt5Vsn?*NL`M z>YI4T_bANY6o=f2-kbs*wZC{jJI+2M4iL8ALmz6v=I_xu7%}61K;izi6Ucp*NJn=? zS;uzM$CsY7jqPo+d-%N{Aatz7%|F01pT~)Jp+7d@th?~A8}a75Xeru;9q*xtpm+8R z(hoRyd8@CivhI4+EnUpi=|u&G&YZL99_}+=QJzoQxo1$rtdymA_B|9vq{oiz!iIaO zuk6UKp@)e&D}0_4VQhwf7GT*@uA3|*FK6d^I5*np=*iQi3tjF_z0_C7a zy!$7pn1EY;f&pH)^=J6X*YM<@q2-(K>7U_AE@0P3@Fe9}^9U894{^gI@+7^T?N6fI z#e0(8IP4d=St=g&3p~k3IOiA01`Nb&eu3bk8$SLE3CO>Qul@p0at1s73Quwc$Nh>D z;RboXg2coly!}^rlF9houPDUJ#43bZ#9vN-MLAbnxMz?sCnfLGmvddmV)x(BkSNRI zaFE)Cu~it_xtV+}4nIsjj}(U1k%6a4hfxRv?*ussioCU(@f*LPnX?O z{B1YbNAKd_yHSu!`y9~EvGP>&@!;Q4Jmob6&;K2Ob_;RU@6fmB;S0Z`IGEfRybX5w z7YbnnT{1aAm*jt;v7=pH(P$e3&gL0<1hWsh$=M`t;bTm?-LS zY8m+?Gt4RMPKL|}r?Bf!G9Mh=V;kYSaOblagPA|5an_AT!Qr8*;770Ni^vHc7Q}jh)LVrjrD+H`my9_6qNn;qJdsH6<9o z-h9CmTeuUGijxJjnPw54zaCoW2YAQ5>3k@pw?j z=1|@)q0q#Vb3kvHgc?Cvsv+~L>{^AWkpH4(Jw(HX%^6(uJS`!eRV186#>Hl zqN5pJfhi#sGrqBV{pc=tVB$RwG?i`7FAxnu9z(R}+=(tP2&F4JWh-xqCctJI;$>~) z+r#QBguR`7JYU=lXqEozR>xTC9Yv%jA5uDkH#2dekQ)3_#|CrT+5_EloiwL{wS;+=Mfq50Fm72Kp1W5hMm1>lX%G!@V4+%;Ept6`z6# zjwE;wDhco&ly5UdcREY=;yn(O7q}!%4%ARtv(GWf#&|za#|(|=!meVP3TXuIFH1`@k?TAsEcLzez#PEIYmfr z@DT45Q+{3^emSLm8f321;AJKS|Ecjb7C^fh@IVS*-0jM$!JtP1JgU1&l&8Z^(sJmfVjN@u99TG3ZxW< z$QL67s7BUmAgrnl63QFQx{oDPBv|?F5-O1rKf($pD#qW~|0_w5fS!?RahPwMsjFYL z)ZeJq<<(C&n?TT7*xFn12rqJ?`g_kE)tMPSSLq%oTkIkz=~v%1qQfm!Ao$){s*b@Q zJ5jzoh)}(#+6oa;z5`=mP!qf;Kq)fwSU0>bZXT0nGA}AfI2M4R#Y*yeS@MeV8+AJ1 zvLe{mmB_rVb_U9&b*%*PUul8d1SnDo1LblICbzpI1a=pG&Y9Y*$c3sJ(SCUAWopQM z2|Z!k+Lvn!)9`Q3R3J!q`1GQd3=|9}K7njl^_LHt8bbbDMSdAV37D;JNM6QEJQV;^ zY4~t2Dh^z|`@JXyN<8c+rKIA5u1V=pHKHcX+BG@oV^pZiAwt#Tq0;mswE=FP%G|u# z4Bxz=54kx&mdJysL(pKYX9#p=PZpuhc2b}g zD$?<>^S*ZH`v&aqNgd%2M5`h7?^mOzfx;GhFgG2Lj=p5r#7X}#)C&nuPhBeP8I$4b zv>J3PY+WgmSZM~_` zL2Qt1oUc`Zv4mR?nh4KTW< z@;KOJgM_Z-+!jghV5R^^^?2>0)37kg;?Sf1;vB6%fzj2IpCjPuT0@F!=;`1YihEIDqx; z1Eme1MN5>KhI5SCfLD0tdOwrKHDyCI^Ih2(ZD8#5TUguX$0QiYrF^YuAV;IMSOM-=izuWg!p7|Da5GgTtxi{-dUv!FF(f)JCFbI3^(IjQ*&P7*3`0Z6h<>mdY}y4Q2sDce@#FTN)p z+$n>INSimL*arrsu)C_8Q4qdZTwFnvnM2dyz0)PTCU>*cz~TB0y;l#J+8d92sY@w+m0PE>6NAUMt8A7$tENZuFs^g&>4$ zKT3%jKMd|iQSQQnzEqNx>d)Phm=vJY5sPJnzm86pFg#x zApNp6cct?@W?kPB%BUGuYu$0u>IGH@g(%fqt@Wwk&~Vmz7IAb~?&V^dI6B<u|A zqPGe}{sIlia0p}(qr1*mI=z-9f(;-+Y?>JxCqR%&Y;egE1?0mCOY+e5;#AEp6wsC} zkb$5&{E+?Q`r;P`L*gKA1?6549LlpzwVX(_h8F?g_6)0$CK)y; zt^7PXGnH)KrpvD#2XS{ZiM#)3f%Fjrfc^2NYMRL-X(ZUDAPSu&nPYTo(t_8JiKMwk zb3wdckmN5-K?C(yhr}Ot>pJ4ASoe}ROJdUMyZR@m>8~l0<=H}^_HmQE-7GyVcaAoz z5wI zL^3`IIwp~fFOXV37&F&tUF7OWqi{Uv_1O079iiDVVp~?K-tQT)vM|;l^rjy6pCh?0 zaJ{1_oCE)~TXHvPi!8E7f@00%$XsDp(|6 z-m9+_4VBZ^KwopNSC?AAZE4~zf_E}~Gp8fV1b*;0SC9&~1ya!tpH3QjC=1I6P36|2koFG}jq0dTEFdje+2RrJ@0BT+j_mRL`@N)yH8%YGxLbiA!{S_1{f*B2Y zumDaG0eP^5cbmzQN49vH0UeZV@kEgkz?@;QXP0nZ5M|&h$uKbgo3?aab^fLkI(J< zyRq@KC~hhN#@IK)5QS1VQ6}7hbJils9e~>f_`?Ju76;qt5IWSUJZ>9_6!P`CtskC) zHsP3V$pkacG2jSh-b!0bAed0tyC)Zq9Yl?wTo&MUgD78dO}`q^Nf#XHfqZ*_iA3R) ze{WbR4}StHr9dmqavSx6l+|n>7EaA{fL*f6zQFH?Q{(t@#@5)})63e^^6k7PZevT6V@Ly_6Z_D=1p-zM-zSaMs3r z?Kgp?eFO22gTYTJ$NeLyA%1T~I9=yK6^1)q7YK*(;UwKSKY~i`XY&%rMRIDbV-0FX z%2N+~5qvFzTG^wkk`2HPzZ^;BAZuS`UJ&W48{zGc?Z_OgeYM2eGpBHUHPhBtbIDYF z3A;s8bE0@tTKzXH(Cg1Kt)W+eQLsAds|b;q>}JE+HtYTw^tCmHVzR1>Lpw;am_*xz2GYEqsCvSP0+u|3f1M)zj4(i8506>kFdBiFvL6bx zoLkpHB81oHo)YYXe!8$GQ*sr`15mzX0l?Q` zeRJfT=mSxn5 z=U<&c#XU3eVl|1ngA9k*1`h!POf-xd;PPvO(Hh@(J=%rihfxuf&o4M<7^N!E_j59* zA42}jTGwOeE`OLBm?e%FB7!`%K=2KGM!CrSR=d;LX?brI^`YnIyS-PWI6Hrvw!BF7 z-Tdarsv@6bIi01|Mg7}yBq#S3`Tm&`bfdP&|KkNInMaEP?=HwrYAzbMWnszYGeyD2 z7FHD$o+}FLe5LJ^&Z6+*Mcs@4EQdUWc~U&~7>%k9$`tgtLps!4=B=Qg`>qgmdp@rypi|cuBgLBK3VPz+wJ6@W+j^f-(9_HtQ0=GRDCikGN>SV88A^JVWee(dn60GK z^;?nRwKtS><{6CQw{BF@bJlG~Y2tDvJ@@DBDF4tNC7pfx9kgx0aV4G8{VuAV`?-=X zSorCCsIBO{l797%_fU7-Jtduc<$a|1@J}VZ_{SY6{;EtxFYPj-w8K6s`nCHND1S_t ziq5~f6K!i6p`ur8szkN(rm5(ab-PiU<4hI3>ez><+wn~mO{30_kU}9irviP~ANQdY zg}}>;UV|=tg0dCq!@cNYbnepvRHCrF<3+DU^>wIO(c11sZ$R(VqfUijxi`HDtv`b# zO8Gu-x)eP+gMyT~C%ox0RC5lcDbw$I(_2yF7bsuZ>f!@L;9Xy$DrNd`AG#dJu`|&PnR%y=mqxYge?x9YVroA7%A8ojgBwpsb{pbUz>H!M! z(m3^}YtYACD8B9&Th$UX;oc?qjy7mXE@}jTxr;nnuf1-kB zFVolk>0_w%FVyKpy9CiE&?X%v@y-nnqU%vxAr<7^H9UxJKvip~6z_onfyRgiX568q)m>(!$wxuD#G{P51ga647`3!{<>E6ttU@?>n zfM2)5rxHGUd$8wi1;}CO;ZhBCQCZL%e!mE%$>(42BZ1#L;r}2wwtiaA%i)3p;Jo$I zdOi-$lY3Y{ne$-+!B{v?qW{)U>-jV|?*`}L^YGbe)F~uc1l#SvS6y^?+YIWf0Jp_2 cXUv&B=jGg`*|VeOgwKtR!i#j&1=0rp1zC#vIRF3v delta 48403 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%0D%WX@h|i>*B3FT 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 Ile+kS09t?b$N&HU diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvossim.metallib b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-tvossim.metallib index ff4d91f2a36496d00fecb2d8e3b4d1da62360e9b..cd99f5a94399d9a7e7da12734fd066250d8c829d 100644 GIT binary patch delta 2065 zcmYjR4NwzT9)J76?j|f@6GB*m!iFGSI8PTsqaC$P0vE8#6k5(vt2Gg5u~m%tG3|_+ z%?1M6pgDssdgsgv7O|bXQ@MhzH+Lb#k2wKp^_+V39Q-)OYp-5#JV$SG3D;)?xfeL^QBS-;T8-AgC8I7CHzW$_y-+QNHy8N5N zr}bB+KWf?h=yT(dgmrgt#Z{!M`n8?@zLM&SH`Y?XqYpQi4xwCk*j~nlYiMTfso|k3wW{2(IhDcZjm|OQ_p5#Lz=LN9=rky6!hON zzZ;LBbveJfpUjY)2D||hAS9|E`qPNVNe$zWJ!g>R-@~C76o|MWbg&ldL_RVwF9k zM4GpyaaUu@_Qthv|3Dw?U=t*z$V}CXom|sZO1cDJ#26(_LApjs1QfrZq{2>aafFhr zFuFlWt03K^NAhq*7-7^BKPm*HaneHf+Cou!gx8-GMg%ocl$2}LP!_wBa=|H~T=9+y z315*CrDOT(eo<|~M`^pId~$SBk%| z6?=}i?=iBYN~?qIurDIm?50K+VJYx3#5)t6h?6iHM??J%_T&$_>-0Xp)o0vMFaFwh z3EDnkh%FOMoye+9%>ouxyW@mMyyz<=EkbYHc8d8|v{AjfZ)}*}Uo>H#t2p?tkX@uP zca;0ok^9ij-|+FR(cJiO-V-~&?gX4TMOyetMsrWlJgCu3c{C408Vh|op8Abv3-D}= z$Y)O>b21IpA?&jHsiJuRKbz)hBz@4QlegVP+4*aSGv7iC8|{X-eTH{UnO5rst6X-j zbkXOv=$lvO&95H6_?wl@3%(oM(lu$2)8ECikcf<2C$q9z>{+`^N6(bKlx6ws%)sF6 zjGX|4-StuLNqC(Zc;)=$Th&9H90L>9BLFvpI)h!+g&Z}59pm<@4@(I%z=PV*WQh|< z=_$QPK~Vd9%O>fsEJ8^fMbzUQCB+v`2u53s2ARFclOO??1JeQk7RdRPS3S*@fw5Fhyep(5@4z10Ggtuu*v9>!mui^&R&Xy_ zX5~Bg@oeOrth`0NfLxH3_s}eP(F0sBpZYWT{=8=B$b|TjleL;E0KgBme~ zY~fr_1F!_DI;`uD^1_>Bi_X2NWkE}$yJ15^vP5ktZCD`holeP-&*hP8hQ7T?#`gUe DS-Y{T delta 1408 zcmYk5eNYr-9LInA!tTQD!SW`HyxblK&S^|7+XDKQKX=WDP z-hrn$@?y)d=}glp4w6#}o=RpJ2fj6-VpbYsGeD(|X^}%S%_v&zdG}8NTtQgRxr@bsnz+txZI}Nt-R`DW7I{Kb-2(mEyuGG)LRx0 z?mC9#PJHA`ROYA?)>U}$#&kfKouJmuU5RtQP0O=0V${AIMmz0clGWEUXmtuza?dcqiZGCjB_TQXb$n07$!SntXy5CPqp4BSk%X-L3s| z9lKXP!bI#s=!8jZoK{GeWY{DHBAx-3L3S|C#-EF*IE9=DQ-}T=(h4zm2{aWJq46ZD ziGJB3EMWrCSf0@gp`O3%vG#15);!WU1c9lre0rU-M%@hwkj56c6g!UW)UytB$e z9v6vkY46HA?vE(SV&2M8YlfOIC$%E=gD@MN(GX7E;!*bc0+c1&%Tblnyu+PJUVIzPp7x;u1g z`jyqH+tcqYMSnh#EXj==CprlzUI%xZa{wESSF+5${NO@d+8%o_FQ$rA`gkll(?bfA zE?zI>Il(V5{xFX*e&Mms>Lhjzf-6-YoI4XjtV3$zxnk7rph9H7P(Q1fUpZPB2rlw4 z{S7{0%EM&L4Y*u9=^bkcN2I&P4UL7=j!BQA6jHk zxDij$=wC5jyK|G?l8C~viUpX1Y68)=09`dTBR@^M_9I=c9lAy>Q1`sV^?ou;4>bM@ DGDOnY diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xros.air b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xros.air index c10afd569d21518784f23550f6f48339c3f08805..a4b68449cf4bab53132017838626d28ee6053706 100644 GIT binary patch delta 48763 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 z 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

BU(Uc-ooT!XIDeQ))It_5ef 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 delta 48404 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 KV^b!5^8WxANdg4` diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xros.metallib b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xros.metallib index e709987f56d5f5f82aa06bca9d9bc13f060c90e6..02e17e6777b00423986f1570e99f27b405bb30ff 100644 GIT binary patch delta 2063 zcmX|B4^R_V9)A0W-A!1+CWNpAB^!d=!gzKeG|HW}N#H=O^gubsQalp@jdy|(|4chm zo6Q1=TCh3exoCA}1x4CUuUL*hz1}z>MC(;RY(1x)(;iyO*z27a9nbM@a^+s=^mg`} z?|t9z_ul)>y!U>8eb4;a< z%-d2Itq)=ghdRBYD77W-B2owT?z#&o~h|z$J91fs%eYo41he%=Xsb9-) zr$1{G?_BHTZn@Y>uEX8oU@Mu9 zG6^i%ahP{Mn)kob|J7*FXS4u9d(?FVWLIp$hnP+`8x(1E`IHa^?44rDhzS0Quom~f zW1&(jKh@Vp@6GEXD^s=q{mjN9!a-5B2X_fwoQL@E*t);isx8xa_N$ejYfuucv%iJ* zSU@M-Ij52<1pw5t0CbEtTqHLmm_SIfb{H>`;}}WG+h)id-}$HW@IyRUL=T<^`3m|U zSGyaJpmjN?zDh<&P6IZ81PF=h`|WAOqojv%$bms*&3AC<1NmZdN^c?=05OC^GU2kv zzD`O~Ndu|@@i|z<8dwDhIithg!_XP%R;h-~kQ$w8aUFG3N^1vP>RwU(+n=3@(pRz5&Ks$6q3guR z6!SDnQ$^m>!2?EiQt5EA2OY}^HrvzeBCPp7hS)ySjZ_jw<78;S$)5c__8EPM@9-P< zHj3~0FT=EV8KPyz(~WH0(JEk3wL4CD#Y_GI(kk@D?dO?)MVr-|`lrU}!-X@B#fp>v z9NAAAb0@iBXYPFmf5XprM04ZgrpFGx;S8KOM_Tz=Msrut+^f;dc{TS#8Y}&FJoRth z7U10)kuRJ>7G)Z$L)gdaCyM4C{Oy`2k@OM2PIli$*`@X4nZHB~TOEdNe#5WKnKs); zHo5eJlI4G>MPI(MWNG#ErQfY1n6VFlaB*YQcNQLJ241x^c?9Yao7z}{)c>NiRV3&6Xy zq1oa}Af>1DA^Ba}KU#N4w^@XeIErY*IZBE@Gb0%7F&bp{A&X_eP*pi099B|5oZpOPt8mr=UoL6Y)ibd-!0P^s{F9?e8ojZGXr zlY9Tp;*InZdwP7(>RXeG_BcM-Z5Keki zhG=J~n`J5~X}pOX!lT?oRgHzXq?KHNXk)rqW;G@Gt4Is+f=+TpzVI6&e%CpNvCCJ0 zM_4qwA@*bUn0X8nB4%{{&OwDsVBM-j(9&g(zxhqT#c#{*GDnECsE>B1ed(edB1Yp6 zP37M9Vz!*W4BG99Vm9WZ9hymi{X9|3$4nTWD6WBoC5jpBp{ZCn!r-a%11@u9Rul8Z zA}gHC(nQ}$Unhsn1}vzJOEez$^O6>eQVyY~)qPxH%h~o75gQf#AshR&^ZA~}745xx zA{#ALutn%YQxkPV(afD)WombEpzo3=D1JJgnvre*T%pjoCNjz1bha95V>OP)O6^Ssad z{_(x}e&{zpIB8C+tX}Pg009~R_AdZQ;K3UQ(_TRYLtoX5&H78(?WG5n=bUu#3{9*5dVuI596TWhP653rSg1OJ+##Q4}D@e;$=L{2_F_h09eBS@HPOzIAw`HR}bWpc;uNVJ-O?X zZ{C4_RgaSiyA&J;aX3wgn3p$g;ll~f1lEFrNUD>$l2EB4HlyI*{x_rxBtJscsh~m~ zkExs<65Q-+GOU3%vlT@9;@hCyYa|GHZH&-wN(1U90f31Ppkt~Uj3_3mS5^f85m@&9 zi;+AQ;HvmPbIhFqA*(v13IOT{yKZDfNHP)N=N%p`aFT|Txd2>8AkfOQrwNP3n|zi3 zpwP7hM8(FV2vQAYeE{Ysl@Rs9J3ZHk#V{qBdg&!N&@@c^3H1P6YoDJ5xDcOVA#3FD zIU+9*0T!~`9^zlNkOs#PPq=bNxZi`#gq>>PMimOpqjzvaBC#@Wgc}bYOxVqj5;psB z!q%8ZXseyt-b=J{Xy2I2G^0?^UN$;Hm)J{MRhO--HvO&S&bZ!M)5;A!xVSHakL$J@Y?Lp6-txaretsTiwA>Y++Gl%@rL=@JxAI5 zgfD{MXM}eneQM0w<}#|WnlFqaVw;=!5#|V6BR?q=mA8U6mG?D2hCL12IMl7}7qkPS z_IyNpksBH(qJ!G+a4@_zXIMUS3}(x9KNB{QR{lXVGisHLI`iRP$MP)u`QGT+ z`Hnit=8pv`)rU{VRuWykB<%M(Gcn4TIPfvk*VZ&2@bPPw6%#8O@*dSHf|q{X`c(P z!IUZHY?%(vMxId3R5zYR>`vMDnu2R2DujZwbbi&5|+S*%C0 z==s&X3IZBze{lv`w!l~JNv?zw?3k{6RuKk(%p!m#&Wx7p2m diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xrossim.air b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xrossim.air index 16d45fb80cdc8cc816b41cc851c858f2f827d6ce..6ecccd2a2b33062a570d5f0c06b0f15de9183ac1 100644 GIT binary patch delta 48764 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 delta 48403 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%0D%WX@h|i>*B3FT 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 Ile+kS09t?b$N&HU diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xrossim.metallib b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels-xrossim.metallib index c9b28398bf88d8fe27ed275f9aec55a389502d91..fabbf86e36303fd65f02a5bf9c4e385f66f4871c 100644 GIT binary patch delta 2065 zcmYjR4NwzT9)J76?j~%)CWNpAB^v_V!gzKeG_|AFB+!5rrqFT@T5S_PT5J^~eoQ-4 zFPjAtwLx-*y6DxJ2pnQNcZ20l?0It+0)E^T5S4T4)w==p81Ksoj_28HuG|Z~&fJ^* zz2E!){=fGz^WOfijhVWCYtmNNRXGrV0tEm;14segO+V7UfHJw~UU^*1AD*#)ee=Y; zkQrEg8`*BX@yUzCzpfV6GFWzM1D;3kt}cIdD+N5daBImZD)ff!rEIu{R)#lP-a_Mp z%V9iM8@^*{M7a?d2GHR#NJZhtrQ1>Q%Vh&ss0|-{b{iUhxE%gkuM9`?DLKnUWrTOH zB*Wi8@YZsSNu$EPw9IzDHQ=`*5GVu&fkUJ~5Tkx8d8r$%*M++rxrjf^oO(yzw3^EE z0+kc-YbX~1#%(8aU+R6IiR3AelL!8G>lu{xw+v?1^NyDs+#MI|Yau@Csru4dZJx%n-gADTMoF~J_G7fy3_9V? z*-ow$08q;U&@sC63b`G@1VWOpU&M=KfhI}$_-!)Vd*g_zfI-L7teL(w9gkzy!h}88c*d&!3Ya z8BqbnkoW>T#YT7v5^{Qnt(T$G(FUo8O_v%SO0kMMrtp>6>*hoXlOc~L~n zIO$=2DC?%lBE@uW;jO-Pl{(YthG{YflZW3=S)aPwk{G z#OR4!<&dbf;FGl7QZ_d^7m*4hQfcjwOW7wXKYa0YjQ$)u<(N&C3tcD9PcctpG*#p| z6*y>MCnFsW_K^KKg3W4ab`h35FGIXF(}g$*gJCi>qYW}L~=}SMWYF+)0sa-vDdO7WFEE9>!nww;1W}7|pfa%0=>GPSE&xS|NFVENy zK-k?F^A5o4Ovfu$CEuzJVqgl)SdRnT4C?fDMGtbq4E9aiD?cb9%m5E+LUYAVAf=`B zBY8p1uPi&HKe7lVaTL*rbCeW+W=1gBCTNh+k30?%a5=Cj0APWfTX8j1tEVcH+O=^) zpG@xF)hZWlr%J;GC0hA%c1nhqEo6i}(~2R)X7uD>vT0qkFsdiwu$e% zFPJW1LezvV%^!+z39L(z2$+Mmc=uoYF85{a!B(J) z#wKbuqF!JnJZ*tpn5*fu`$hFb5P;|LVDT%LG)pvI{wy_NY+nPGwxErX+#2OxhA6`O z1k!!pl!jYf=S_@sn^;eYDzpIr3|`l3$MUa|4&%{-wx_TH0U+pVIr0vgDOcRbb@J%%$#?Txp(6w0J5JVWDi45ms3qUtGKc4`nU@Ra zb{c@SP}O2RzmYfGBHOg?9c`=In%zxrG$l*a8zoJv<)e!!*>cQCZW;ah7CC$Pzi`K^ Af&c&j delta 1409 zcmYk5e@qi+7{}lD?zp?sD;x#nkRQELT&g3>L2Je6#ucHPMwY6u7^miH-4qps={D!a zdM!|e$qzl^%_Yk^HOiK_0adHhFfs}2?q>In>oXo- zxH)>Ev8ji?TsoP2;Da4~>3?g=O4e zvA8kmOC%3q6JMjUNSU&3!uoE`1^9(}rEc*?O!$3H_Rfn}`WDdYZ8x2&?9QT;S%leE zf;+Xe;!RAe_uCHmZ4iMWa0G#fLy#fAjT&gf+F7NgI0uH*nqSB^kCYL|Ie0l8FnDFN z*)?gGkK_{DN6x(Z4J~lTk8PGdr59@KcO=HdMuZ5@nCM8RUDB^Tl>cJDUdg$@p)RI7 z2_zTpcd)*3u`d&Ho#11PbFiIDF(>EXI$eYh17HmY!0P~jjC-c(r)WVTPQ;$bO7E~s z{bv|HC?BO`b`dxQrr`u76Hd{vOZ3M)16T)g1Bo{7N=zln#8j9(`QMOs5Pu&>Q^86! zoM8_git{Z~Cy*<6ZJ_k2esZ6$z=G9m&!aew*9RRDRkrx5^b?`!`& zp_8Iz`2Wmj&QftMfIb4P`NGM`I5UQcXwM=5h=L{^R=S4*phpBitIk}aOd4nWfcR#f z{aFxc#8eYqq58vA zwkH5gbce+!K5wE^to3iMsVXvRG!uv>{o0 zF`&I9^p8@(KJ94|T&vBzrk*_w)7AHXq*x3jsV_nvzX_kv! z!Si#kY*t+Eh^Gww`DCIrFLs>hBtXeFaKCi{fMZE=j@erfSdNMN6OQM{R}pd_2Q}Gl zf}eJBIzHb4d_3(7bCCA&Gg_+yKR5`S>AJw;`5?#?i!B^mg4!p@AQ9o~pDE!sjurU> zE8TRY!OPFO>8!(QxVxX?Uv;tvU%Dqq0%wk#&M;zuhCPokCh~H zyl9J*Vx;HICOx#b!wX{v1Ev}GQ5Ww^!hN(|*f%R3mANvpUa}f>a(;u%u#9iQkS{c| zg!ZZQ5=^+qonN6N)3HZP4SmJaZk&j&l3I=Hf0A0mOzRSED}6)Mp^gD3XRfhN#Dw;y zD@C}skQi_C60-mwf_H6^PcLmj(Km8-TWzEo*`MIX96LJc?l E3&wiZ`2YX_ diff --git a/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels.metallib b/Sources/UntoldEngine/UntoldEngineKernels/UntoldEngineKernels.metallib index ba04641afd0dbe73eeab12c61b57fa3e7be30cdb..379ad6841996a5f614826c2dd4e32b0e492f5cfd 100644 GIT binary patch delta 2072 zcmX|B4^&gv8Ncrj@4bXacnKjq5|tMMG%!aW1S786CV>K0S%XsN)UJs@#ZEDz<*a9O zd3gz7DY(>7AJTgI1VrZ69a>tz+2aWTt$#pR>pbn5-B3Hm{yDU+vs*fLA3fbY=XbvE z_xrx@-S516?>BwI{N^dMuCjWg3lUJD0w7=n0KB>BRo&w#$GpAiL4h^@2=Vl&?Y{lY z2k*D9T6cc?4vW!v_+P*L;*md9oG5ys@oZ)3=4}-4=pzkoJ4z1dO@}Dlz^9FS!noUI#?7L9@ zD@6*n*jrod2m}g&LEsPx5X4A}ooo)GrTR#RD-#Ju*Ol^~R$p-BRD5w$I0?LT+fzpT;51g@3iDe(^t74xm>Y{5C zwO@T?XA%CepgMqi_;#+IINQ1LclJulH9Y;LvX3<=iB>yaN4qSb8SZQ<D27sikL{cr= z%0Qd&Zl*QhvbNsObp%9fSSu7-n|E4OdF7yf*@hYQ5b~cM6nz^dcGf#wE1o`K8!IHMM=lYDz@fwa-Rj&O_~x9a=&ab8X2Dsq(?%0ibSXPxFtWd9@| z59G=*dLmOjAgFElB<-}7%udeAVy-L}RSkI5J%akRCr`%ctJn$G^+YMxb9{J;c^IRq zJl~1dLq>K|4!PLF&ea5)USH=SY*~JW*frCElo3YbWO%^E_I(}uh<@1`3K$R82yX>0 z!n9v7#NL_O4rI&j1|Ey4y$gg-xDd!8ZG7*7<23WnSe<%n|I`@WmOJBIF1xHBBTb}n z#U%HhYsI|NdOctb#a1kgWjt_NcbtTUbEM5W%V=)%n$O&tIiF@etg+EQUr79mZ?E*$ zcS%nA-bIp0h$asQbkcMk#(rztSn3ZD!%nB+`GDaCbE@5b&Mp{pvls_d@IGRHx!smE26U(b{DDedg*b?hS5Ol-~*a<)F z)Jy{G>3D&4A_K$Y1#W1tcmadm%P8QFFnHqPfX5u2)lB#&qQx#|WxTiB-^^jNEf&)K0~|$;Lixexj?UIM|~n($OLrTZZ1tsHLvUnmJa) zljqpES$DIuMbO*@t*{>-w(!9P?IMkruA(L~g2iC*`)G}v=~f?Lh&;T9C%wbwWZdl; zHZ$TU#5zhyLU#gy!QxCL7GX(yi4ArGoN5d%#Jss9r ztV}nh_MJ%hbCWc^A%RBDNsh~8n)LaG1ifT2ldKfHOnR~#U{Wf?H&&V2R2Be1s44ey z$wsQDEt2UXmW+?U4kF77O_KHsSuOceNL4NMbf7{}lD?zlVX6_4`aL4jT=Zq*obrM2R8<65weX-!2~j8k*9bISY}Zt9qm z^je^PM4+yWnuQ^_V+ESf zZLw8xvwYW1wTXHc3-)z_d7QtLEI`Ko`@U2GWanIavRQ*p~~r z6W|EOIoK2PF&F3HPPhp_2EcL-fOpV_Y3~~*UZ4eqBoTWihvxj~R-D)2AC-6M1TFy` zU;>U>{<+o5eta>A(t57@lV31`=+XOpHg#FaH}71St=3G!>Mi@dT>re#y=+r31;( zq&I>He7X}Vx(gJyUIPGn3jh;gpyn#7wKyT8TOJ93Ff9G*m2f@}NM!!c9Jgme$Rk1{ z0kE+z_`~!tO(z246cO=4E1l7k2f!r^L6s8!hB73%QU=8xy{-jBCx%r*#Vx>sq+)8L z^se$#jG6^gBk$ELfJ)T>HBI^FXUf#;AE1uGGypC(O$`NtU(7VnRmz(^RK7P140Nm6 zFTQG^Gc5fgWy^~TzxZk>GgB)JM;Sby{YdDSsYUs5;jZss0@plDVE*F-PS(X)qnX*> zMb!y-_idYQJjy_G>2RDaHW$|+$~!A{e?{}gZR?lU3H^`7-Rxkjhg#XO#Y~(M@avR! z^#k`&k~AB4vgEqqCg>nnWq%fBlJgp(+1owxL0^D07kXK;da8HJQu;pS4wH8{X;Zix z#f%L$Es9kg*2d)qJNF&Tm9~X1M;Ru6A*(Cvt{H`G)gK7BUDYG0dS%tQuL;33Vo*4dfuv7 zP8H4jb~#pcX~t~l)xocqZk;oFWlO7+siYOr(=n-0f%EVz&8leTTK%Ce%lzr)b6t@$ z=#fyqYtZXN`=3rMn$4n$z6U7V03J5a0PtR#TwwGSg%@JtuGDr@$`(TI<6v^Whu|k% zoSHW|fS;%RQ4Z36{<+HH!1u<1D_b3&n*$<@U2NjmGSogsMu-?+|56#Z`d&#OT<)P` z4L*L-L+8y6xLh3J9chUsth>ey_LY#06CPPEA@@&sBve`_S~zYI?Qis!)v}UAj+CsE zGW2xTWZI}L2+!Wz8#X-mw7GeI8t$iULhYo~CUZ`)Ub5)bvZ+C4SjIo5GYR`yLiIv= z9j4yr&XlUjZ0r$JLl1a^`my11sae1BhSVHon&)wE(^o|`>gaIt`4Ia|ObzZED8YTj z#7K*en2e}5OCX~5v2p8Z8`uoC@c<;wVNohu&;TANk49h~+!_l0tyPYmrI@r2Q7;!M yu@b+{97Q@wXqC=5Rhyn*YZw5GsA}+5Gdf72ydtgg=@qI{IWAJx-c462OT)i070Md` diff --git a/Tests/UntoldEngineRenderTests/GaussianRenderingTest.swift b/Tests/UntoldEngineRenderTests/GaussianRenderingTest.swift index 00f964c7a..f00c90625 100644 --- a/Tests/UntoldEngineRenderTests/GaussianRenderingTest.swift +++ b/Tests/UntoldEngineRenderTests/GaussianRenderingTest.swift @@ -448,6 +448,143 @@ final class GaussianRenderingTest: BaseRenderSetup { "Sorted passes should equal total passes") } + // MARK: - Depth occlusion against opaque geometry + + // + // These tests deliberately avoid assuming which exact screen pixel the splat cloud + // projects to (a real .ply point cloud isn't necessarily centered on its entity's + // transform origin). Instead, a large cube is used as an occluder, sized and + // positioned so its near face covers the entire view frustum near the camera — its + // depth is written at *every* pixel, so it occludes the splat wherever it actually + // renders, without needing pixel-perfect alignment. "Not occluded" is verified by the + // splat's own baseline visibility (maxAlphaAnywhere) with no occluder present at all. + + // A sphere large enough (relative to its distance) to cover the whole frame puts the + // camera inside its own volume — its surface then faces away from the camera and gets + // back-face culled, i.e. it stops rendering entirely. A cube's flat near face avoids + // that: as long as the camera sits in front of that face (not inside the cube), it + // fully blocks the view within its angular footprint regardless of size. + @discardableResult + private func addFullFrameOccludingCube(at position: simd_float3, extent: Float = 8.0) -> EntityID { + let entity = createEntity() + var meshes = BasicPrimitives.createCube(extent: extent) + // BasicPrimitives meshes carry no material (ModelIO primitives don't set one), + // and combinedModelLightExecution silently skips any submesh with `material == nil` + // (RenderPasses.swift:2262) — no draw call, no depth write. Assign a plain opaque + // material so this occluder actually renders instead of being invisibly skipped. + let defaultMaterial = Material(runtimeMaterial: RuntimeMaterialSource(), device: renderInfo.device) + for meshIndex in meshes.indices { + for submeshIndex in meshes[meshIndex].submeshes.indices { + meshes[meshIndex].submeshes[submeshIndex].material = defaultMaterial + } + } + if let renderComponent = scene.assign(to: entity, component: RenderComponent.self) { + renderComponent.mesh = meshes + renderComponent.assetURL = URL(fileURLWithPath: "/dev/null/occluder.untold") + } + if let local = scene.get(component: LocalTransformComponent.self, for: entity) { + local.position = position + local.boundingBox = Mesh.computeMeshBoundingBox(for: meshes) + } + if let world = scene.get(component: WorldTransformComponent.self, for: entity) { + var space = matrix_identity_float4x4 + space.columns.3 = simd_float4(position, 1.0) + world.space = space + } + setVisibleEntities() + return entity + } + + /// Scans the whole texture for the maximum alpha value found anywhere — a + /// registration-independent stand-in for "is the splat visible somewhere in frame." + private func maxAlphaAnywhere(in texture: MTLTexture) -> Float { + precondition(texture.pixelFormat == .rgba16Float, "Test assumes the Gaussian target is rgba16Float") + let width = texture.width + let height = texture.height + let bytesPerPixel = 8 + let bytesPerRow = width * bytesPerPixel + let dataSize = bytesPerRow * height + let rawData = UnsafeMutableRawPointer.allocate(byteCount: dataSize, alignment: 1) + defer { rawData.deallocate() } + texture.getBytes(rawData, bytesPerRow: bytesPerRow, from: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0) + let ptr = rawData.bindMemory(to: Float16.self, capacity: width * height * 4) + var best: Float = 0 + for i in 0 ..< (width * height) { + let a = Float(ptr[i * 4 + 3]) + if a > best { best = a } + } + return best + } + + private func renderAndReadMaxAlpha() -> Float { + renderer.draw(in: renderer.metalView) + let expectation = XCTestExpectation(description: "Gaussian render") + var result: Float = -1 + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + result = self.maxAlphaAnywhere(in: renderInfo.gaussianRenderPassDescriptor.colorAttachments[Int(0)].texture!) + expectation.fulfill() + } + wait(for: [expectation], timeout: TimeInterval(timeoutFactor)) + return result + } + + /// Baseline: the test splat, alone, is visible somewhere in frame. Establishes that + /// the occlusion tests below have something real to occlude. + func testGaussianOcclusion_splatAloneIsVisible() { + let camera = createTestCamera() + cameraLookAt(entityId: camera, eye: simd_float3(0, 3, 7), target: simd_float3(0, 0, 0), up: simd_float3(0, 1, 0)) + + let alpha = renderAndReadMaxAlpha() + XCTAssertGreaterThan(alpha, 0.05, "❌ Splat alone with no occluder should be visible somewhere in frame, got \(alpha)") + } + + /// A splat behind a closer opaque object covering the whole frame must be fully + /// occluded. Regression test for the depth-read added to `fragmentGaussianTBDRShader` + /// (previously the draw pipeline had `depthCompareFunction: .always, depthEnabled: false` + /// and never read the opaque depth at all, so splats always rendered on top regardless + /// of geometry in front). + func testGaussianOcclusion_hiddenBehindCloserOpaqueMesh() { + let eye = simd_float3(0, 3, 7) + let target = simd_float3(0, 0, 0) + + let camera = createTestCamera() + cameraLookAt(entityId: camera, eye: eye, target: target, up: simd_float3(0, 1, 0)) + + // Cube center placed so its near face (facing the camera) sits ~1 unit in front + // of the camera, well outside the cube itself, with a wide enough face to cover + // the frame at that distance. + let nearPoint = eye + 0.657 * (target - eye) // distance ≈5.0 from eye; near face ≈1.0 + addFullFrameOccludingCube(at: nearPoint, extent: 8.0) + + let alpha = renderAndReadMaxAlpha() + XCTAssertLessThan( + alpha, 0.05, + "❌ Splat behind a closer full-frame opaque mesh should be fully occluded, got maxAlpha=\(alpha)" + ) + } + + /// The mirror case: a large opaque object placed *beyond* the splat (farther from + /// the camera) must not occlude it, even though it's big enough to otherwise span the + /// whole frame — exercises the actual depth comparison, not just "no occluder at all." + func testGaussianOcclusion_notOccludedByFartherOpaqueMesh() { + let eye = simd_float3(0, 3, 7) + let target = simd_float3(0, 0, 0) + + let camera = createTestCamera() + cameraLookAt(entityId: camera, eye: eye, target: target, up: simd_float3(0, 1, 0)) + + // Well beyond the splat on the same eye→target line (near face still farther + // from the camera than the target) — must NOT occlude it, regardless of size. + let farPoint = eye + 2.0 * (target - eye) + addFullFrameOccludingCube(at: farPoint, extent: 8.0) + + let alpha = renderAndReadMaxAlpha() + XCTAssertGreaterThan( + alpha, 0.05, + "❌ Splat should remain visible when the opaque mesh is farther away, got maxAlpha=\(alpha)" + ) + } + // MARK: - Helper Methods func createTestCamera() -> EntityID { diff --git a/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift b/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift new file mode 100644 index 000000000..31c1212cd --- /dev/null +++ b/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift @@ -0,0 +1,423 @@ +// +// GaussianStreamingTest.swift +// UntoldEngine +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import simd +@testable import UntoldEngine +import XCTest + +/// Verifies that Gaussian-splat props (`StreamingComponent.assetKind == .gaussianSplat`) +/// participate correctly in `GeometryStreamingSystem`'s distance-based load/unload lifecycle, +/// share `MemoryBudgetManager`'s geometry budget and LRU eviction pool with streamed meshes, +/// and are kept `markUsed` while visible so they aren't evicted ahead of genuinely stale +/// entities. Mirrors the CPU-side control-flow style of `GeometryStreamingEvictionTests`. +@MainActor +final class GaussianStreamingTest: BaseRenderSetup { + override func setUp() async throws { + try await super.setUp() + + GeometryStreamingSystem.shared.reset() + GeometryStreamingSystem.shared.enabled = true + GeometryStreamingSystem.shared.updateInterval = 0.0 // no throttle + GeometryStreamingSystem.shared.maxConcurrentLoads = 3 + GeometryStreamingSystem.shared.enableFrustumGate = false // deterministic candidate detection + GeometryStreamingSystem.shared.maxQueryRadius = 500.0 + GeometryStreamingSystem.shared.visibleEvictionProtectionRadius = 30.0 + + MemoryBudgetManager.shared.clear() + MemoryBudgetManager.shared.enabled = true + MemoryBudgetManager.shared.geometryBudget = 100 * 1024 * 1024 // 100 MB + MemoryBudgetManager.shared.textureBudget = 0 + MemoryBudgetManager.shared.highWaterMark = 0.85 + MemoryBudgetManager.shared.lowWaterMark = 0.70 + + visibleEntityIds = [] + } + + override func tearDown() async throws { + GeometryStreamingSystem.shared.reset() + GeometryStreamingSystem.shared.enabled = false + GeometryStreamingSystem.shared.updateInterval = 0.1 + GeometryStreamingSystem.shared.enableFrustumGate = true + MemoryBudgetManager.shared.clear() + visibleEntityIds = [] + try await super.tearDown() + } + + // MARK: - Helpers + + /// Create a tile-owned, `.unloaded` streaming entity configured for the gaussian path, + /// registered in the octree so `update()`'s spatial query can discover it. Mirrors + /// `GeometryStreamingEvictionTests.makeUnloadedEntity`. + @discardableResult + private func makeUnloadedGaussianEntity( + distance: Float, + streamingRadius: Float = 10.0, + unloadRadius: Float = 20.0, + filename: String = "test_gaussians", + withExtension ext: String = "ply" + ) -> EntityID { + let tileRoot = createEntity() + if let tileComp = scene.assign(to: tileRoot, component: TileComponent.self) { + // Keep this stub out of the tile-parse candidate loop entirely — it exists + // only to satisfy loadMesh's isTileOwned ownership guard. + tileComp.state = .parsed + } + + let entityId = createEntity() + + if let local = scene.assign(to: entityId, component: LocalTransformComponent.self) { + local.boundingBox = ( + min: simd_float3(distance - 0.5, -0.5, -0.5), + max: simd_float3(distance + 0.5, 0.5, 0.5) + ) + } + if let world = scene.assign(to: entityId, component: WorldTransformComponent.self) { + world.space = simd_float4x4(1.0) + } + if let scenegraph = scene.assign(to: entityId, component: ScenegraphComponent.self) { + scenegraph.parent = tileRoot + scenegraph.level = 1 + } + if let streaming = scene.assign(to: entityId, component: StreamingComponent.self) { + streaming.state = .unloaded + streaming.assetKind = .gaussianSplat + streaming.assetFilename = filename + streaming.assetExtension = ext + streaming.streamingRadius = streamingRadius + streaming.unloadRadius = unloadRadius + } + + OctreeSystem.shared.registerEntity(entityId) + return entityId + } + + /// Create an entity that already looks loaded via the gaussian streaming path — state + /// `.loaded`, tracked in the streaming system's loaded set, carrying a `GaussianComponent`, + /// and registered with `MemoryBudgetManager` — without going through a real async PLY + /// load. Mirrors `GeometryStreamingEvictionTests.makeLoadedEntity`. + @discardableResult + private func makeLoadedGaussianEntity( + distance: Float, + memoryBytes: Int, + streamingRadius: Float = 10.0, + unloadRadius: Float = 20.0 + ) -> EntityID { + let entityId = createEntity() + + if let local = scene.assign(to: entityId, component: LocalTransformComponent.self) { + local.boundingBox = ( + min: simd_float3(distance - 0.5, -0.5, -0.5), + max: simd_float3(distance + 0.5, 0.5, 0.5) + ) + } + if let world = scene.assign(to: entityId, component: WorldTransformComponent.self) { + world.space = simd_float4x4(1.0) + } + if let streaming = scene.assign(to: entityId, component: StreamingComponent.self) { + streaming.state = .loaded + streaming.assetKind = .gaussianSplat + streaming.assetFilename = "dummy_splat" + streaming.assetExtension = "ply" + streaming.streamingRadius = streamingRadius + streaming.unloadRadius = unloadRadius + streaming.lastVisibleFrame = 1 + } + _ = scene.assign(to: entityId, component: GaussianComponent.self) + + GeometryStreamingSystem.shared.registerLoadedEntity(entityId) + MemoryBudgetManager.shared.registerMesh( + entityId: entityId, + meshSizeBytes: memoryBytes, + textureSizeBytes: 0 + ) + + return entityId + } + + // MARK: - StreamingAssetKind default + + func testStreamingComponentDefaultsToMeshAssetKind() { + let component = StreamingComponent() + XCTAssertEqual(component.assetKind, .mesh, "❌ Default assetKind should be .mesh so existing mesh streaming call sites are unaffected") + } + + // MARK: - setEntityGaussianStreamable / findTileEntity + + /// End-to-end check of the consolidated API: given a positioned entity and a tile + /// whose bounds contain that position, `setEntityGaussianStreamable` should parent it + /// under that tile, register it with the octree, and configure it as a real gaussian + /// streaming candidate — equivalent to `makeUnloadedGaussianEntity`'s manual assembly, + /// but via the one public call callers are meant to use. + func testSetEntityGaussianStreamable_attachesToContainingTileAndStreamsCorrectly() async throws { + let tileRoot = createEntity() + if let tileComp = scene.assign(to: tileRoot, component: TileComponent.self) { + tileComp.state = .parsed + } + if let tileLocal = scene.get(component: LocalTransformComponent.self, for: tileRoot) { + tileLocal.boundingBox = (min: simd_float3(-10, -10, -10), max: simd_float3(10, 10, 10)) + } + OctreeSystem.shared.registerEntity(tileRoot) + + let entity = createEntity() + translateTo(entityId: entity, position: .zero) + + setEntityGaussianStreamable( + entityId: entity, + filename: "test_gaussians", + withExtension: "ply", + streamingRadius: 10.0, + unloadRadius: 20.0, + boundingBoxHalfExtent: simd_float3(1, 1, 1) + ) + + XCTAssertEqual( + scene.get(component: ScenegraphComponent.self, for: entity)?.parent, tileRoot, + "❌ Entity should be parented under the tile whose bounds contain its position" + ) + + let streaming = scene.get(component: StreamingComponent.self, for: entity) + XCTAssertEqual(streaming?.assetKind, .gaussianSplat, "❌ assetKind should be set to .gaussianSplat") + XCTAssertEqual(streaming?.assetFilename, "test_gaussians") + XCTAssertEqual(streaming?.assetExtension, "ply") + XCTAssertEqual(streaming?.streamingRadius, 10.0) + XCTAssertEqual(streaming?.unloadRadius, 20.0) + XCTAssertEqual(streaming?.state, .unloaded, "❌ Entity should start unloaded, same as any other streaming candidate") + + // Drive a real update() tick and confirm it streams in exactly like a manually + // assembled gaussian streaming entity would. + GeometryStreamingSystem.shared.update(cameraPosition: .zero, deltaTime: 0.1) + await scene.get(component: StreamingComponent.self, for: entity)?.loadTask?.value + + XCTAssertEqual(scene.get(component: StreamingComponent.self, for: entity)?.state, .loaded, "❌ Entity should load once registered and in range") + XCTAssertNotNil(scene.get(component: GaussianComponent.self, for: entity), "❌ GaussianComponent should be attached after load") + } + + /// If no tile contains the entity's position, the entity should be left as a plain, + /// non-streaming entity rather than crashing or silently half-configuring it. + func testSetEntityGaussianStreamable_noContainingTileLeavesEntityNonStreaming() { + let entity = createEntity() + translateTo(entityId: entity, position: simd_float3(1000, 1000, 1000)) // no tile out here + + setEntityGaussianStreamable( + entityId: entity, + filename: "test_gaussians", + withExtension: "ply", + boundingBoxHalfExtent: simd_float3(1, 1, 1) + ) + + XCTAssertNil( + scene.get(component: StreamingComponent.self, for: entity), + "❌ Entity should not get a StreamingComponent when no containing tile is found" + ) + } + + /// `findTileEntity` should return the tile whose bounds contain the query point, and + /// `nil` when nothing does. + func testFindTileEntity_returnsContainingTileOrNil() { + let tileRoot = createEntity() + _ = scene.assign(to: tileRoot, component: TileComponent.self) + if let tileLocal = scene.get(component: LocalTransformComponent.self, for: tileRoot) { + tileLocal.boundingBox = (min: simd_float3(-5, -5, -5), max: simd_float3(5, 5, 5)) + } + OctreeSystem.shared.registerEntity(tileRoot) + + XCTAssertEqual(findTileEntity(containing: .zero), tileRoot, "❌ Should find the tile containing the origin") + XCTAssertNil(findTileEntity(containing: simd_float3(1000, 1000, 1000)), "❌ Should return nil when no tile contains the point") + } + + // MARK: - Load lifecycle + + func testGaussianEntityOutsideStreamingRadiusStaysUnloaded() { + let entity = makeUnloadedGaussianEntity(distance: 500.0, streamingRadius: 10.0, unloadRadius: 20.0) + + GeometryStreamingSystem.shared.update(cameraPosition: .zero, deltaTime: 0.1) + + let streaming = scene.get(component: StreamingComponent.self, for: entity) + XCTAssertEqual(streaming?.state, .unloaded, "❌ Entity far outside its streaming radius must stay unloaded") + XCTAssertNil(scene.get(component: GaussianComponent.self, for: entity), "❌ No GaussianComponent should be attached before the entity streams in") + } + + func testGaussianEntityLoadsWhenCameraApproaches() async { + let entity = makeUnloadedGaussianEntity(distance: 0.0, streamingRadius: 10.0, unloadRadius: 20.0) + + GeometryStreamingSystem.shared.update(cameraPosition: .zero, deltaTime: 0.1) + + // loadMesh dispatched an async Task onto StreamingComponent.loadTask — await it + // directly rather than polling, since Task exposes `.value`. + let task = scene.get(component: StreamingComponent.self, for: entity)?.loadTask + await task?.value + + let streaming = scene.get(component: StreamingComponent.self, for: entity) + XCTAssertEqual(streaming?.state, .loaded, "❌ Entity within streaming radius should finish loading") + + let gaussian = scene.get(component: GaussianComponent.self, for: entity) + XCTAssertNotNil(gaussian, "❌ GaussianComponent should be attached after a successful streamed load") + XCTAssertGreaterThan(gaussian?.splatCount ?? 0, 0, "❌ Loaded splat count should be nonzero") + + XCTAssertTrue( + MemoryBudgetManager.shared.isTracked(entityId: entity), + "❌ Streamed gaussian entity should be registered with MemoryBudgetManager, same as a streamed mesh" + ) + } + + func testGaussianEntityUnloadsWhenCameraLeaves() { + let entity = makeLoadedGaussianEntity(distance: 0.0, memoryBytes: 1024, streamingRadius: 10.0, unloadRadius: 20.0) + visibleEntityIds = [] + + let farCameraPosition = simd_float3(1000, 0, 0) + for _ in 0 ..< 3 { + GeometryStreamingSystem.shared.update(cameraPosition: farCameraPosition, deltaTime: 0.1) + } + + let streaming = scene.get(component: StreamingComponent.self, for: entity) + XCTAssertEqual(streaming?.state, .unloaded, "❌ Entity beyond unloadRadius should unload") + XCTAssertNil(scene.get(component: GaussianComponent.self, for: entity), "❌ GaussianComponent should be removed on unload") + XCTAssertFalse( + MemoryBudgetManager.shared.isTracked(entityId: entity), + "❌ MemoryBudgetManager entry should be removed on unload so budget/eviction accounting stays accurate" + ) + } + + func testGaussianEntityReloadsAfterCameraReturns() async { + let entity = makeUnloadedGaussianEntity(distance: 0.0, streamingRadius: 10.0, unloadRadius: 20.0) + + // First approach: load. + GeometryStreamingSystem.shared.update(cameraPosition: .zero, deltaTime: 0.1) + await scene.get(component: StreamingComponent.self, for: entity)?.loadTask?.value + + XCTAssertEqual(scene.get(component: StreamingComponent.self, for: entity)?.state, .loaded, "Pre-condition: first load should succeed") + XCTAssertNotNil(scene.get(component: GaussianComponent.self, for: entity), "Pre-condition: GaussianComponent should be attached after first load") + + // Retreat: unload. + visibleEntityIds = [] + let farCameraPosition = simd_float3(1000, 0, 0) + for _ in 0 ..< 3 { + GeometryStreamingSystem.shared.update(cameraPosition: farCameraPosition, deltaTime: 0.1) + } + XCTAssertEqual(scene.get(component: StreamingComponent.self, for: entity)?.state, .unloaded, "Pre-condition: entity should unload when far") + XCTAssertNil(scene.get(component: GaussianComponent.self, for: entity), "Pre-condition: GaussianComponent should be removed on unload") + + // Re-approach: should load again. + GeometryStreamingSystem.shared.update(cameraPosition: .zero, deltaTime: 0.1) + let secondTask = scene.get(component: StreamingComponent.self, for: entity)?.loadTask + await secondTask?.value + + let streaming = scene.get(component: StreamingComponent.self, for: entity) + XCTAssertEqual(streaming?.state, .loaded, "❌ Entity should reload after camera re-enters streaming radius") + + let gaussian = scene.get(component: GaussianComponent.self, for: entity) + XCTAssertNotNil(gaussian, "❌ GaussianComponent should be re-attached after reload") + XCTAssertGreaterThan(gaussian?.splatCount ?? 0, 0, "❌ Reloaded splat count should be nonzero") + } + + // MARK: - Tile ownership / tile unload interaction + + /// Regression test: `GeometryStreamingSystem.unloadTile` tears down every scenegraph + /// descendant of a parsed tile via `collectTileDescendants` — necessary for the tile's + /// own regenerable mesh content, but a gaussian-splat prop parented under the tile + /// (the pattern `GameScene.addPoolTableSplat` uses to satisfy `isTileOwned`) must + /// survive that sweep rather than being destroyed, since it is never regenerated by + /// the tile's own parse pipeline. Without the assetKind == .gaussianSplat guard in + /// collectTileDescendants, this destroys the splat entity outright the first time the + /// surrounding tile unloads — after that, nothing recreates it, so it never streams + /// back in no matter how many times the camera re-approaches. + func testGaussianSplatSurvivesParentTileUnload() { + let tileEntity = createEntity() + guard let tileComp = scene.assign(to: tileEntity, component: TileComponent.self) else { + XCTFail("Failed to assign TileComponent") + return + } + tileComp.state = .parsed + tileComp.tileId = "test_tile" + + // A regular mesh-kind child, representing tile-owned regenerable content — + // this one SHOULD still be destroyed on tile unload. Uses setParent (not manual + // ScenegraphComponent.parent assignment) since getEntityChildren reads the + // parent's `children` array, which only setParent populates. + let meshChild = createEntity() + setParent(childId: meshChild, parentId: tileEntity) + if let streaming = scene.assign(to: meshChild, component: StreamingComponent.self) { + streaming.assetKind = .mesh + streaming.state = .loaded + } + + // A gaussian-splat prop attached the same way GameScene.addPoolTableSplat does — + // this one must survive. + let splatChild = createEntity() + setParent(childId: splatChild, parentId: tileEntity) + if let streaming = scene.assign(to: splatChild, component: StreamingComponent.self) { + streaming.assetKind = .gaussianSplat + streaming.state = .loaded + } + _ = scene.assign(to: splatChild, component: GaussianComponent.self) + + GeometryStreamingSystem.shared.unloadTile(entityId: tileEntity) + + XCTAssertFalse(scene.exists(meshChild), "❌ Regular mesh-kind tile child should still be destroyed on tile unload") + XCTAssertTrue(scene.exists(splatChild), "❌ Gaussian-splat child must survive tile unload — it is not tile-owned regenerable content") + XCTAssertNotNil(scene.get(component: GaussianComponent.self, for: splatChild), "❌ Splat's GaussianComponent should be untouched by tile unload") + XCTAssertEqual( + scene.get(component: StreamingComponent.self, for: splatChild)?.state, .loaded, + "❌ Splat's own streaming state should be unaffected by tile unload — it's governed independently by its own radii" + ) + } + + // MARK: - Shared eviction pool + + func testFarGaussianEntityIsEvictedUnderMemoryPressureAheadOfCloseOne() { + let closeEntity = makeLoadedGaussianEntity(distance: 5.0, memoryBytes: 50 * 1024 * 1024, streamingRadius: 500, unloadRadius: 500) + let farEntity = makeLoadedGaussianEntity(distance: 200.0, memoryBytes: 51 * 1024 * 1024, streamingRadius: 500, unloadRadius: 500) + + // Both visible; total 101 MB > 85 MB high-water mark on a 100 MB budget. + visibleEntityIds = [closeEntity, farEntity] + + XCTAssertTrue(MemoryBudgetManager.shared.shouldEvict(), "Pre-condition: budget should be over threshold") + + GeometryStreamingSystem.shared.update(cameraPosition: .zero, deltaTime: 1.0) + + let closeState = scene.get(component: StreamingComponent.self, for: closeEntity)?.state + let farState = scene.get(component: StreamingComponent.self, for: farEntity)?.state + + XCTAssertEqual(closeState, .loaded, "❌ Close visible splat (within visibleEvictionProtectionRadius) must not be evicted") + XCTAssertEqual(farState, .unloaded, "❌ Far visible splat must be evicted through the same evictLRU pool meshes use") + XCTAssertNil(scene.get(component: GaussianComponent.self, for: farEntity), "❌ Evicted splat's GaussianComponent should be torn down") + } + + // MARK: - markUsed correctness + + /// `executeGaussianFrustumCulling` must call `MemoryBudgetManager.markUsed` for entities + /// that survive its per-splat frustum test, since gaussian-only entities are structurally + /// excluded from the RenderComponent-keyed query that normally feeds `markUsed` + /// (see CullingSystem.swift). Without this, a loaded, on-screen splat would look + /// permanently stale to `evictLRU`. + func testVisibleGaussianEntityIsMarkedUsedByFrustumCulling() { + let entity = createEntity() + setEntityGaussian(entityId: entity, filename: "test_gaussians", withExtension: "ply") + XCTAssertNotNil(scene.get(component: GaussianComponent.self, for: entity), "Pre-condition: gaussian asset should load synchronously") + + // setEntityGaussian already registers with MemoryBudgetManager at the current frame; + // capture that baseline so we can detect markUsed advancing it once rendering runs. + let beforeFrame = MemoryBudgetManager.shared.getLastUsedFrame(for: entity) + + let expectation = XCTestExpectation(description: "Gaussian render pass completes") + renderer.draw(in: renderer.metalView) + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + expectation.fulfill() + } + wait(for: [expectation], timeout: TimeInterval(timeoutFactor)) + + let afterFrame = MemoryBudgetManager.shared.getLastUsedFrame(for: entity) + XCTAssertNotNil(afterFrame, "❌ Entity should still be tracked by MemoryBudgetManager after rendering") + XCTAssertNotEqual( + afterFrame, beforeFrame, + "❌ markUsed should have advanced lastUsedFrame for a splat entity that survived frustum culling this frame" + ) + } +} diff --git a/docs/API/UsingGaussianSystem.md b/docs/API/UsingGaussianSystem.md index 22316bff2..0ab4d40f0 100644 --- a/docs/API/UsingGaussianSystem.md +++ b/docs/API/UsingGaussianSystem.md @@ -39,3 +39,85 @@ Once everything is set up: 2. Your Gaussian Splat model will appear in the game window. 3. If the model is not visible or appears incorrect, revisit the file path and format to ensure everything is loaded correctly. +--- + +## Streaming Gaussian Splats in Large Scenes + +`setEntityGaussian` loads a splat immediately and keeps it resident for the lifetime of the +entity — fine for a small number of always-visible splats, but not what you want for props +scattered across a large tile-streamed scene (chairs, tables, decor inside a streamed +building). Loading every one of those up front defeats the point of streaming, and the +engine has no way to unload them again on its own. + +For that case, use `setEntityGaussianStreamable` instead. It registers the entity with +`GeometryStreamingSystem`, which loads and unloads it automatically based on camera +distance — the same way it already handles the surrounding streamed tile geometry. + +### Prerequisites + +This only makes sense in a scene that is already using tile-based streaming — i.e. one +loaded with `setEntityStreamScene` (see [Using the Geometry Streaming System](UsingGeometryStreamingSystem.md)). +`setEntityGaussianStreamable` attaches the splat to whichever tile stub's bounds contain +the entity's position, so it needs those tile stubs to already exist. Call it **after** +`setEntityStreamScene`'s completion handler has fired — tile stubs are guaranteed to be +registered by then. + +### Step 1: Create and position the entity + +Position and orient the entity *before* registering it for streaming — the position at the +time you call `setEntityGaussianStreamable` is what determines which tile it gets attached +to. + +```swift +let streamSplat = createEntity() +translateTo(entityId: streamSplat, position: simd_float3(2.0, 0.0, -4.0)) +rotateTo(entityId: streamSplat, angle: 180.0, axis: simd_float3(1.0, 0.0, 0.0)) +``` + +### Step 2: Register it for streaming + +```swift +setEntityGaussianStreamable( + entityId: streamSplat, + filename: "chair", + withExtension: "ply", + streamingRadius: 30.0, + unloadRadius: 45.0, + boundingBoxHalfExtent: simd_float3(0.5, 0.8, 0.5) +) +``` + +Parameters: + +- `entityId`: The entity created and positioned in Step 1. +- `filename` / `withExtension`: Same as `setEntityGaussian` — the `.ply` file to stream in + once the camera is in range. +- `streamingRadius`: Distance from the camera at which the splat starts loading. +- `unloadRadius`: Distance beyond which the splat unloads. Should be larger than + `streamingRadius` to avoid load/unload thrashing at the boundary. +- `boundingBoxHalfExtent`: A local-space half-extent for the entity, roughly matching the + splat's real-world size. **This has no default and must be supplied.** Without a real + bounding box, the streaming system's frustum gate collapses to a zero-extent point at + the entity's exact position — the splat may load once but then fail to reliably + re-stream in after the camera moves away and back, because re-entry depends on the + camera looking at that exact point rather than anywhere near the prop. +- `priority`: Optional. Higher-priority entities load first when multiple candidates are + in range at once. Defaults to `0`. + +> Note: If no tile is found containing the entity's position, `setEntityGaussianStreamable` +> logs a warning and leaves the entity as a plain, non-streaming entity (no `StreamingComponent` +> is attached) — it will not crash, but it also will not load. Double-check the position +> against the streamed scene's tile bounds if this happens. + +### Which function should I use? + +- **`setEntityGaussian`** — load immediately, stays resident. Use for a small number of + splats that should always be visible (e.g. a single hero object, a standalone demo scene). +- **`setEntityGaussianAsync`** — same immediate/resident behavior, but the parse and GPU + upload happen off the main thread. Use for a one-off splat load where you don't want a + frame hitch, but still don't need distance-based unloading. +- **`setEntityGaussianStreamable`** — registers the entity with `GeometryStreamingSystem` + instead of loading anything itself. Use for splat props inside a large, tile-streamed + scene, where you want the same load-when-near/unload-when-far behavior the rest of the + scene already gets. + From e3ea54257f46c9b215aca525ba8e2e388a5f926d Mon Sep 17 00:00:00 2001 From: Untold Engine Date: Wed, 5 Aug 2026 19:11:58 -0700 Subject: [PATCH 3/8] [Chores] Formatted files --- Sources/Sandbox/GameScene.swift | 18 ------------------ .../GaussianStreamingTest.swift | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Sources/Sandbox/GameScene.swift b/Sources/Sandbox/GameScene.swift index 442777197..a4457fa2b 100644 --- a/Sources/Sandbox/GameScene.swift +++ b/Sources/Sandbox/GameScene.swift @@ -49,24 +49,6 @@ */ // Uncomment to render a streamed scene - - let sceneRoot = createEntity() - setEntityStreamScene(entityId: sceneRoot, url: URL(string: "https://d8pyi1c08k1w.cloudfront.net/dungeon3/dungeon3.json")!){ success in - //setSceneReady(success) - } - - let streamSplat = createEntity() - - Task { - await setEntityGaussianAsync( - entityId: streamSplat, - filename: "/Users/haroldserrano/Downloads/Pooltable", - withExtension: "ply" - ) { success in - rotateTo(entityId: streamSplat, angle: 180.0, axis: simd_float3(1.0,0.0,0.0)) - setSceneReady(success) - } - } } private func configureEngineSystems() { diff --git a/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift b/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift index 31c1212cd..a86693303 100644 --- a/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift +++ b/Tests/UntoldEngineRenderTests/GaussianStreamingTest.swift @@ -155,7 +155,7 @@ final class GaussianStreamingTest: BaseRenderSetup { /// under that tile, register it with the octree, and configure it as a real gaussian /// streaming candidate — equivalent to `makeUnloadedGaussianEntity`'s manual assembly, /// but via the one public call callers are meant to use. - func testSetEntityGaussianStreamable_attachesToContainingTileAndStreamsCorrectly() async throws { + func testSetEntityGaussianStreamable_attachesToContainingTileAndStreamsCorrectly() async { let tileRoot = createEntity() if let tileComp = scene.assign(to: tileRoot, component: TileComponent.self) { tileComp.state = .parsed From 37bc6e1887ad25ce669c6cff6b1bdcb39e8dadb0 Mon Sep 17 00:00:00 2001 From: Untold Engine Date: Wed, 5 Aug 2026 21:14:55 -0700 Subject: [PATCH 4/8] [Release] Preparing Release 0.15.0 --- CHANGELOG.md | 44 +++++++++++++++++++ README.md | 2 +- Sources/Demos/ShowcaseDemo/AppDelegate.swift | 2 +- Sources/Sandbox/AppDelegate.swift | 2 +- .../UntoldEngine/Renderer/UntoldEngine.swift | 2 +- docs/API/GettingStarted.md | 2 +- 6 files changed, 49 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e077b720d..2b302d6e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,48 @@ # Changelog +## v0.15.0 - 2026-08-06 +### 🐞 Fixes +- [Patch] Added material fidelity report to the exporter (a36166f…) +- [Patch] Added untoldengine export-tiles CLI command with RAM-aware parallel export (50221e3…) +- [Patch] Fix HDR IBL energy calibration (fcda745…) +- [Patch] Fix iOS simulator Metal kernel build output (2ccc688…) +- [Patch] fixed abs path in serializer (7f8dd45…) +- [Patch] Improve directional shadow softness for XR (ac5a1ff…) +- [Patch] Default quaternion rotations to identity instead of zero (06651fa…) +- [Patch] Improve Blender-style light units, attenuation, and shadow behavior (a08fded…) +- [Patch] Added debug views for hdr (95a9c6f…) +- [Patch] Stage world/studio-light HDR environments and transcode EXR to ZIP for engine compatibility (b782fd4…) +- [Patch] Re-encode all file-backed material textures through Blender instead of raw-copying (6332c8f…) +- [Patch] Stage world/studio-light HDR environments in export-tiles too (2e351f2…) +- [Patch] Fixed bake resolution (ee582e4…) +- [Patch] Added new variable to setRendering (990520d…) +- [Patch] Fix joint transforms buffer CPU/GPU race with per-frame ring (f07c0fd…) +- [Patch] Restore encoder safety net with a closed flag (e8f9607…) +- [Patch] Persist scene-authored color management and environment state (8980058…) +- [Patch] Aggregate getAnimationPolicy over descendants (bee8546…) +- [Patch] Fix MPS -destination must be writable- crash on sRGB texture resample (7924e4a…) +- [Patch] Create the camera component in CameraNode when missing (aeefa4c…) +- [Patch] Address review: redundant changeAnimation is a no-op (5f61789…) +### 📚 Docs +- [Docs] Reorder topics in docs (30cf39e…) +- [Docs] Updated usage example (5c1bc45…) +- [Docs] Updated engine documentation (f9a88b0…) +- [Docs] Documented bake-materials and bake-color-management (a2696cf…) +- [Docs] Updated IBL usage documentation (845039d…) +- [Docs] Update the readme docs (96d0340…) +- [Docs] Updated xr images (6ac8f82…) +- [Docs] Added scene authored documentation (f9f0ea9…) +- [Docs] add plugin architecture and authoring guidelines (e363a83…) +- [Docs] Added community links (140edc0…) +### 🚀 Features +- [Feature] Add compiled animation clip sampling and pose layer foundation (8e3892b…) +- [Feature] Add per-frame update event with UntoldView onUpdate modifier (6c3dd20…) +- [Feature] Add simulator fallback for the TBDR deferred renderer (d78c8c1…) +- [Feature] Add per-entity animation policy (inherit/forceOn/forceOff) (24e9ad8…) +- [Feature] Add primitive, light, and camera nodes to the scene builder DSL (3b8bc92…) +- [Feature] Add physics backend plugin interface (260b23f…) +- [Feature] Add inertialized animation transitions to changeAnimation (08be3a6…) +- [Feature] Drive external physics backends via an EngineExtension coordinator (c3b8b0e…) +- [Feature] Mesh and Gaussian share streaming path (0e088a9…) ## v0.14.3 - 2026-07-17 ### 🐞 Fixes - [Patch] Added the new public debug view - position (3dccbad…) diff --git a/README.md b/README.md index c36f3acb9..dce78ace0 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ Clone the repository and launch the Starter Demo: ```bash git clone https://github.com/untoldengine/UntoldEngine.git cd UntoldEngine -git checkout v0.14.3 +git checkout v0.15.0 swift run StarterDemo ``` diff --git a/Sources/Demos/ShowcaseDemo/AppDelegate.swift b/Sources/Demos/ShowcaseDemo/AppDelegate.swift index a49a463bb..7435f2e21 100644 --- a/Sources/Demos/ShowcaseDemo/AppDelegate.swift +++ b/Sources/Demos/ShowcaseDemo/AppDelegate.swift @@ -11,7 +11,7 @@ @MainActor final class AppDelegate: NSObject, NSApplicationDelegate { private enum Constants { - static let appVersion = "0.14.3" + static let appVersion = "0.15.0" static let defaultWindowSize = NSSize(width: 1920, height: 1080) static let minimumWindowSize = NSSize(width: 640, height: 480) } diff --git a/Sources/Sandbox/AppDelegate.swift b/Sources/Sandbox/AppDelegate.swift index 96d6b880c..a42966f19 100644 --- a/Sources/Sandbox/AppDelegate.swift +++ b/Sources/Sandbox/AppDelegate.swift @@ -10,7 +10,7 @@ @MainActor final class AppDelegate: NSObject, NSApplicationDelegate { private enum Constants { - static let appVersion = "0.14.3" + static let appVersion = "0.15.0" static let windowSize = NSSize(width: 1600, height: 900) } diff --git a/Sources/UntoldEngine/Renderer/UntoldEngine.swift b/Sources/UntoldEngine/Renderer/UntoldEngine.swift index 2310b7b57..be7d9dc49 100644 --- a/Sources/UntoldEngine/Renderer/UntoldEngine.swift +++ b/Sources/UntoldEngine/Renderer/UntoldEngine.swift @@ -197,7 +197,7 @@ public class UntoldRenderer: NSObject, MTKViewDelegate { BatchingSystem.shared.applyRuntimeBatchingTuning(.macOSBalanced) #endif - Logger.log(message: "Untold Engine Starting. Version 0.14.3") + Logger.log(message: "Untold Engine Starting. Version 0.15.0") } public func initSizeableResources() { diff --git a/docs/API/GettingStarted.md b/docs/API/GettingStarted.md index 4e9a00a1f..576451d45 100644 --- a/docs/API/GettingStarted.md +++ b/docs/API/GettingStarted.md @@ -23,7 +23,7 @@ Clone the repository and launch the demo: ```bash git clone https://github.com/untoldengine/UntoldEngine.git cd UntoldEngine -git checkout v0.14.3 +git checkout v0.15.0 swift run ShowcaseDemo ``` From 4ff5040b19c37dfa49d42ed21096fbd7ed2e3d56 Mon Sep 17 00:00:00 2001 From: Untold Engine Date: Wed, 5 Aug 2026 23:09:19 -0700 Subject: [PATCH 5/8] [Chores] Updated Readme and index page --- README.md | 46 +++++++++++++++++++++------------ docs/API/UsingGaussianSystem.md | 1 + docs/index.md | 12 --------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index dce78ace0..510c58f49 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Request a Feature · Ask a Question + · + Join Our Discord

@@ -45,7 +47,9 @@ Untold Engine is built for developers and teams who: Creator & Lead Developer: https://www.haroldserrano.com -![untoldengine-image](/docs/images/engine-highlight-5.png) +[![untoldengine-image](/docs/images/engine-highlight-5.png)](https://vimeo.com/1176995991?fl=ip&fe=ec) + +Click to Play --- @@ -78,6 +82,13 @@ workflow, product experience, and customer-specific behavior. +## Requirements + +- macOS 26 or newer +- Swift 6 / Xcode 26 or newer +- Apple Silicon Mac +- Supported platforms: macOS, iOS, and visionOS + ## Try the Engine Right Now The best first step is to run the Starter Demo. It is intentionally small and @@ -164,22 +175,7 @@ Untold Engine is well-suited for: --- -# Commercial Use, Sponsored Features, and Support - -Untold Engine is open source under MPL-2.0 and can be used in commercial apps. -Paid commercial options are available for teams that need private engine -modifications, sponsored engine features, priority support, or custom terms. - -- **Sponsored open-source features** — fund roadmap-aligned engine work that is - released into the public MPL engine. -- **Commercial license** — keep private modifications to engine internals closed - under commercial terms. -- **Priority support / retainers** — get focused help with engine integration, - rendering issues, performance, and production use. - -See [COMMERCIAL.md](COMMERCIAL.md) for commercial licensing details. ---- # Engine API @@ -298,6 +294,23 @@ Full license: https://www.mozilla.org/MPL/2.0/ --- +# Commercial Use, Sponsored Features, and Support + +Untold Engine is open source under MPL-2.0 and can be used in commercial apps. +Paid commercial options are available for teams that need private engine +modifications, sponsored engine features, priority support, or custom terms. + +- **Sponsored open-source features** — fund roadmap-aligned engine work that is + released into the public MPL engine. +- **Commercial license** — keep private modifications to engine internals closed + under commercial terms. +- **Priority support / retainers** — get focused help with engine integration, + rendering issues, performance, and production use. + +See [COMMERCIAL.md](COMMERCIAL.md) for commercial licensing details. + +--- + # Trademark "Untold Engine" and the Untold Engine logo are trademarks of **Untold Engine Studios**. Forks may not use the name in a way that implies official endorsement. See [TRADEMARKS.md](TRADEMARKS.md). @@ -308,4 +321,3 @@ Full license: https://www.mozilla.org/MPL/2.0/ - [GitHub Discussions](https://github.com/untoldengine/UntoldEngine/discussions) — ideas and questions - [GitHub Issues](https://github.com/untoldengine/UntoldEngine/issues) — bugs and tasks -- [Discord](https://discord.gg/pSckCPFxj) — ask questions, share projects, and discuss engine development diff --git a/docs/API/UsingGaussianSystem.md b/docs/API/UsingGaussianSystem.md index 0ab4d40f0..29f14ea96 100644 --- a/docs/API/UsingGaussianSystem.md +++ b/docs/API/UsingGaussianSystem.md @@ -11,6 +11,7 @@ Start by creating an entity that represents your Gaussian Splat object. ```swift let myEntity = createEntity() ``` + --- ### Step 2: Link a Gaussian Splat to the Entity diff --git a/docs/index.md b/docs/index.md index 90712d8b5..bfba49de2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,18 +13,6 @@ Ask a Question
-
-
- -![Build Status](https://github.com/untoldengine/UntoldEngine/actions/workflows/ci-build-test.yml/badge.svg?style=flat-square) -[![Project license](https://img.shields.io/github/license/untoldengine/UntoldEngine.svg?style=flat-square)](https://github.com/untoldengine/UntoldEngine/blob/main/LICENSE) -[![Pull Requests welcome](https://img.shields.io/badge/PRs-welcome-ff69b4.svg?style=flat-square)](https://github.com/untoldengine/UntoldEngine/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) -![Version](https://img.shields.io/github/v/release/untoldengine/UntoldEngine?style=flat-square&label=version) -![Commits](https://img.shields.io/github/commit-activity/t/untoldengine/UntoldEngine?style=flat-square&label=commits) -![Last Commit](https://img.shields.io/github/last-commit/untoldengine/UntoldEngine?style=flat-square&label=last+commit) - -
- --- # Untold Engine From a5b9844b2d0e4ba8d2e911bf6701e7677460c8b0 Mon Sep 17 00:00:00 2001 From: Javier Segura Date: Tue, 4 Aug 2026 10:33:09 +0200 Subject: [PATCH 6/8] [Refactor] Share per-mesh vertex-stream binding across render passes Replace the nine open-coded per-mesh vertex buffer bind sites in the model-family and shadow-family passes with bindModelVertexStreams / bindShadowVertexStreams encoder helpers. Batched-geometry sites keep their raw batch-buffer binds. Also add a reader regression test covering unknown core-range chunk types so future format chunks cannot brick older runtimes. Groundwork for the deformation compute pass: the helpers are the single point where deformed position/normal buffers will replace the base streams. --- .../RenderExtensionModelSurface.swift | 54 +--- .../UntoldEngine/Renderer/RenderPasses.swift | 240 +----------------- .../Renderer/RenderVertexStreamBinding.swift | 90 +++++++ .../UntoldEngineTests/NativeFormatTests.swift | 16 ++ 4 files changed, 115 insertions(+), 285 deletions(-) create mode 100644 Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift diff --git a/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift b/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift index c64f85c79..97ab709be 100644 --- a/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift +++ b/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift @@ -494,59 +494,7 @@ private func drawModelSurfaceMeshes( index: Int(modelPassFragmentUniformIndex.rawValue) ) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = getEntityComponent(entityId: entityId, componentType: SkeletonComponent.self) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassVerticesIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassNormalIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassUVIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassTangentIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointWeightsIndex.rawValue) - ) - - if let jointTransformBuffer { - renderEncoder.setVertexBuffer( - jointTransformBuffer, - offset: 0, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) for submesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( diff --git a/Sources/UntoldEngine/Renderer/RenderPasses.swift b/Sources/UntoldEngine/Renderer/RenderPasses.swift index 02a57cc3c..f1929a759 100644 --- a/Sources/UntoldEngine/Renderer/RenderPasses.swift +++ b/Sources/UntoldEngine/Renderer/RenderPasses.swift @@ -1177,28 +1177,7 @@ public enum RenderPasses { &modelUniforms, length: MemoryLayout.stride, index: Int(shadowPassModelUniform.rawValue) ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(shadowPassModelPositionIndex.rawValue)].buffer, - offset: 0, index: Int(shadowPassModelPositionIndex.rawValue) - ) - - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(shadowPassHasArmature.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, index: Int(shadowPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, index: Int(shadowPassJointWeightsIndex.rawValue) - ) - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(shadowPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(shadowPassJointTransformIndex.rawValue)) - } + renderEncoder.bindShadowVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( @@ -1391,31 +1370,7 @@ public enum RenderPasses { modelUniforms.projectionMatrix = renderInfo.perspectiveSpace renderEncoder.setVertexBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(shadowPassModelUniform.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(shadowPassModelPositionIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassModelPositionIndex.rawValue) - ) - - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(shadowPassHasArmature.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointWeightsIndex.rawValue) - ) - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(shadowPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(shadowPassJointTransformIndex.rawValue)) - } + renderEncoder.bindShadowVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( @@ -1545,31 +1500,7 @@ public enum RenderPasses { modelUniforms.projectionMatrix = renderInfo.perspectiveSpace renderEncoder.setVertexBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(shadowPassModelUniform.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(shadowPassModelPositionIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassModelPositionIndex.rawValue) - ) - - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(shadowPassHasArmature.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointWeightsIndex.rawValue) - ) - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(shadowPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(shadowPassJointTransformIndex.rawValue)) - } + renderEncoder.bindShadowVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( @@ -1770,48 +1701,7 @@ public enum RenderPasses { &modelUniforms, length: MemoryLayout.stride, index: Int(modelPassUniformIndex.rawValue) ) - // Only enable armature path when a valid joint transform buffer exists. - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(modelPassHasArmature.rawValue)) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassVerticesIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassNormalIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, - index: Int(modelPassUVIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassTangentIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassJointIdIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassJointWeightsIndex.rawValue) - ) - - if let jointTransformBuffer { - renderEncoder.setVertexBuffer(jointTransformBuffer, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identityMatrix, length: MemoryLayout.stride, index: Int(modelPassJointTransformIndex.rawValue)) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) renderEncoder.setFragmentBytes( &modelUniforms, length: MemoryLayout.stride, index: Int(modelPassFragmentUniformIndex.rawValue) @@ -2238,23 +2128,7 @@ public enum RenderPasses { renderEncoder.setVertexBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(modelPassUniformIndex.rawValue)) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(modelPassHasArmature.rawValue)) - - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, offset: 0, index: Int(modelPassVerticesIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, offset: 0, index: Int(modelPassNormalIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, index: Int(modelPassUVIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, offset: 0, index: Int(modelPassTangentIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointIdIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointWeightsIndex.rawValue)) - - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(modelPassJointTransformIndex.rawValue)) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) renderEncoder.setFragmentBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(modelPassFragmentUniformIndex.rawValue)) @@ -3644,59 +3518,7 @@ public enum RenderPasses { index: Int(modelPassUniformIndex.rawValue) ) - // Only enable armature path when a valid joint transform buffer exists. - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassVerticesIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassNormalIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassUVIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassTangentIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointWeightsIndex.rawValue) - ) - if let jointTransformBuffer { - renderEncoder.setVertexBuffer( - jointTransformBuffer, - offset: 0, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) renderEncoder.setFragmentBytes( &modelUniforms, @@ -3916,30 +3738,7 @@ public enum RenderPasses { index: Int(modelPassUniformIndex.rawValue) ) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, offset: 0, index: Int(modelPassVerticesIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, offset: 0, index: Int(modelPassNormalIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, index: Int(modelPassUVIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, offset: 0, index: Int(modelPassTangentIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointIdIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointWeightsIndex.rawValue)) - if let jointTransformBuffer { - renderEncoder.setVertexBuffer(jointTransformBuffer, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitives( @@ -4155,30 +3954,7 @@ public enum RenderPasses { index: Int(modelPassUniformIndex.rawValue) ) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, offset: 0, index: Int(modelPassVerticesIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, offset: 0, index: Int(modelPassNormalIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, index: Int(modelPassUVIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, offset: 0, index: Int(modelPassTangentIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointIdIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointWeightsIndex.rawValue)) - if let jointTransformBuffer { - renderEncoder.setVertexBuffer(jointTransformBuffer, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) if let edgeIndexBuffer = mesh.featureEdgeIndexBuffer, mesh.featureEdgeIndexCount > 0 { renderEncoder.setTriangleFillMode(.fill) diff --git a/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift b/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift new file mode 100644 index 000000000..6cd42c3a1 --- /dev/null +++ b/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift @@ -0,0 +1,90 @@ + +// +// RenderVertexStreamBinding.swift +// UntoldEngine +// +// Shared per-mesh vertex-stream binding for the model-family and shadow-family +// render passes. +// + +import CShaderTypes +import Metal +import MetalKit +import simd + +/// Mesh.metalKitMesh.vertexBuffers is laid out in model-descriptor order +/// (ModelPassBufferIndices 0-5) regardless of which pass consumes it; the +/// shadow pass sources from that same array but binds to its own slots. +extension MTLRenderCommandEncoder { + func bindModelVertexStreams(mesh: Mesh, entityId: EntityID) { + let slots: [ModelPassBufferIndices] = [ + modelPassVerticesIndex, + modelPassNormalIndex, + modelPassUVIndex, + modelPassTangentIndex, + modelPassJointIdIndex, + modelPassJointWeightsIndex, + ] + for slot in slots { + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(slot.rawValue)].buffer, + offset: 0, + index: Int(slot.rawValue) + ) + } + bindJointStreams( + mesh: mesh, + entityId: entityId, + hasArmatureIndex: Int(modelPassHasArmature.rawValue), + jointTransformIndex: Int(modelPassJointTransformIndex.rawValue) + ) + } + + func bindShadowVertexStreams(mesh: Mesh, entityId: EntityID) { + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, + offset: 0, + index: Int(shadowPassModelPositionIndex.rawValue) + ) + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, + offset: 0, + index: Int(shadowPassJointIdIndex.rawValue) + ) + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, + offset: 0, + index: Int(shadowPassJointWeightsIndex.rawValue) + ) + bindJointStreams( + mesh: mesh, + entityId: entityId, + hasArmatureIndex: Int(shadowPassHasArmature.rawValue), + jointTransformIndex: Int(shadowPassJointTransformIndex.rawValue) + ) + } + + private func bindJointStreams( + mesh: Mesh, + entityId: EntityID, + hasArmatureIndex: Int, + jointTransformIndex: Int + ) { + // Only enable armature path when a valid joint transform buffer exists. + let jointTransformBuffer = mesh.skin?.jointTransformsBuffer + var hasArmature = getEntityComponent(entityId: entityId, componentType: SkeletonComponent.self) != nil + && jointTransformBuffer != nil + setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: hasArmatureIndex) + + if let jointTransformBuffer { + setVertexBuffer(jointTransformBuffer, offset: 0, index: jointTransformIndex) + } else { + var identityMatrix = matrix_identity_float4x4 + setVertexBytes( + &identityMatrix, + length: MemoryLayout.stride, + index: jointTransformIndex + ) + } + } +} diff --git a/Tests/UntoldEngineTests/NativeFormatTests.swift b/Tests/UntoldEngineTests/NativeFormatTests.swift index 822167909..b33e70738 100644 --- a/Tests/UntoldEngineTests/NativeFormatTests.swift +++ b/Tests/UntoldEngineTests/NativeFormatTests.swift @@ -264,6 +264,22 @@ final class NativeFormatTests: XCTestCase { } } + func testUnknownCoreChunkTypeIsIgnored() throws { + // A core-range chunk type this runtime does not know (e.g. one added by a + // newer format revision) must not prevent the rest of the asset from loading. + let futureChunkType = UntoldChunkType(rawValue: 22) + let fixture = makeTinyFixture(pluginChunks: [ + (futureChunkType, Data([0x01, 0x02, 0x03, 0x04]), 0), + ]) + + let decoded = try UntoldReader().readAsset(from: fixture.fileData) + + XCTAssertTrue(decoded.chunks.contains(where: { $0.chunkType == futureChunkType })) + XCTAssertTrue(decoded.pluginChunks.isEmpty) + XCTAssertEqual(decoded.meshes.count, 1) + XCTAssertEqual(decoded.entities.count, 1) + } + func testColorManagementRoundtripsThroughRuntimeLoader() throws { // Reuses the tiny fixture's existing texture (index 0, "albedo.ktx2") // as the LUT reference, to exercise the same index -> URL resolution From b3149b115aa2db7c7ffbee26c6eae4bf8e4a8511 Mon Sep 17 00:00:00 2001 From: Javier Segura Date: Thu, 6 Aug 2026 08:37:59 +0200 Subject: [PATCH 7/8] Add standard license header to RenderVertexStreamBinding.swift --- .../UntoldEngine/Renderer/RenderVertexStreamBinding.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift b/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift index 6cd42c3a1..b3c50d187 100644 --- a/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift +++ b/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift @@ -1,11 +1,12 @@ - // // RenderVertexStreamBinding.swift // UntoldEngine // -// Shared per-mesh vertex-stream binding for the model-family and shadow-family -// render passes. +// Copyright (C) Untold Engine Studios // +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. import CShaderTypes import Metal From 9ce94c78f345b7e18767e36d4658c9a4c6cfaabe Mon Sep 17 00:00:00 2001 From: Javier Segura Date: Wed, 5 Aug 2026 20:24:13 +0200 Subject: [PATCH 8/8] [Feature] Add UntoldViewOptions for runtime view settings in SwiftUI Adds a runtime-tunable options struct for the SwiftUI host view, per the UntoldViewOptions proposal (docs/proposals/UntoldViewOptions.md): - UntoldViewOptions (Equatable, Sendable): preferredFramesPerSecond, isPaused, clearColor. Settable via UntoldView(renderer:options:) or the .preferredFramesPerSecond(_:)/.paused(_:)/.options(_:) modifiers. - SceneView's Coordinator now owns the renderer: a fallback renderer is created exactly once instead of on every SwiftUI body re-evaluation, and options are diffed against the last-applied copy so only changed properties touch the live MTKView. The renderer is never recreated. - UntoldView builds its scene content once, when the platform view is created (after the renderer exists, so mesh loading has a Metal device), instead of on every re-evaluation of the view struct. - SceneView.onInit is deferred accordingly: the block now runs once when the platform view is created rather than immediately at body-eval time on every re-evaluation. UntoldRendererConfig stays immutable, create-time only. --- .../Scenes/Builder/UntoldView.swift | 57 +++- Sources/UntoldEngine/Scenes/SceneView.swift | 133 ++++++-- .../Scenes/UntoldViewOptions.swift | 47 +++ .../UntoldViewOptionsTests.swift | 108 ++++++ docs/proposals/UntoldViewOptions.md | 321 ++++++++++++++++++ 5 files changed, 630 insertions(+), 36 deletions(-) create mode 100644 Sources/UntoldEngine/Scenes/UntoldViewOptions.swift create mode 100644 Tests/UntoldEngineTests/UntoldViewOptionsTests.swift create mode 100644 docs/proposals/UntoldViewOptions.md diff --git a/Sources/UntoldEngine/Scenes/Builder/UntoldView.swift b/Sources/UntoldEngine/Scenes/Builder/UntoldView.swift index f6ce8dce7..b8c6fe1e5 100644 --- a/Sources/UntoldEngine/Scenes/Builder/UntoldView.swift +++ b/Sources/UntoldEngine/Scenes/Builder/UntoldView.swift @@ -13,19 +13,41 @@ import SwiftUI @MainActor public struct UntoldView: View { - @State private var metalView: MTKView private var renderer: UntoldRenderer? - private var content: [any NodeProtocol] = [] + var options: UntoldViewOptions + private var contentBuilder: @MainActor () -> [any NodeProtocol] private var updateHandler: (@MainActor (UpdateEvent) -> Void)? - public init(renderer: UntoldRenderer? = nil, @SceneBuilder _ content: @escaping @MainActor () -> [any NodeProtocol]) { - self.renderer = renderer ?? UntoldRenderer.create() - metalView = self.renderer?.metalView ?? MTKView() - self.content = content() + /// - Parameters: + /// - renderer: An externally owned renderer, or nil to let the view + /// create one. Either way the renderer is resolved once and survives + /// SwiftUI re-evaluations of this struct. + /// - options: Runtime-tunable view settings (target FPS, pause, clear + /// color). When SwiftUI re-evaluates the view with changed options, + /// only the difference is applied to the live `MTKView` — the + /// renderer and the scene are never recreated. Also settable through + /// the `options(_:)` / `preferredFramesPerSecond(_:)` / `paused(_:)` + /// modifiers. + /// - content: Scene content, built exactly once when the underlying + /// platform view is created (after the renderer is ready, so mesh + /// loading has a Metal device). + public init( + renderer: UntoldRenderer? = nil, + options: UntoldViewOptions = .default, + @SceneBuilder _ content: @escaping @MainActor () -> [any NodeProtocol] + ) { + self.renderer = renderer + self.options = options + contentBuilder = content } public var body: some View { - SceneView(renderer: renderer, updateHandler: updateHandler) + SceneView( + renderer: renderer, + options: options, + setup: { _ = contentBuilder() }, + updateHandler: updateHandler + ) } /// Subscribes to the engine's per-frame update event (RealityKit @@ -41,4 +63,25 @@ public struct UntoldView: View { copy.updateHandler = handler return copy } + + /// Replaces all runtime view options. + public func options(_ options: UntoldViewOptions) -> UntoldView { + var copy = self + copy.options = options + return copy + } + + /// Sets the target frame rate of the live view. + public func preferredFramesPerSecond(_ fps: Int) -> UntoldView { + var copy = self + copy.options.preferredFramesPerSecond = fps + return copy + } + + /// Pauses or resumes the draw loop of the live view. + public func paused(_ paused: Bool) -> UntoldView { + var copy = self + copy.options.isPaused = paused + return copy + } } diff --git a/Sources/UntoldEngine/Scenes/SceneView.swift b/Sources/UntoldEngine/Scenes/SceneView.swift index 3657e6905..bb1b66eb1 100644 --- a/Sources/UntoldEngine/Scenes/SceneView.swift +++ b/Sources/UntoldEngine/Scenes/SceneView.swift @@ -17,32 +17,86 @@ import SwiftUI #endif public struct SceneView: ViewRepresentable { - var mtkView: MTKView private var renderer: UntoldRenderer? + private var options: UntoldViewOptions private var updateHandler: (@MainActor (UpdateEvent) -> Void)? + private var setupHandler: (@MainActor () -> Void)? - // TODO: Maybe we should thow an error on init instead of allowing nil renderer value - public init(renderer: UntoldRenderer? = nil, updateHandler: (@MainActor (UpdateEvent) -> Void)? = nil) { - self.renderer = renderer ?? UntoldRenderer.create() + public init( + renderer: UntoldRenderer? = nil, + options: UntoldViewOptions = .default, + setup: (@MainActor () -> Void)? = nil, + updateHandler: (@MainActor (UpdateEvent) -> Void)? = nil + ) { + self.renderer = renderer + self.options = options + setupHandler = setup self.updateHandler = updateHandler - mtkView = self.renderer!.metalView } - /// Persists across SwiftUI re-inits of the view struct; owns the frame-event - /// subscription so it is created once and cancelled on dismantle. + /// Persists across SwiftUI re-inits of the view struct. Owns the renderer + /// (so a fallback-created one is made exactly once, not on every body + /// re-evaluation), the one-shot setup, the frame-event subscription, and + /// the last options applied to the MTKView. @MainActor public final class Coordinator { + var renderer: UntoldRenderer? var handler: (@MainActor (UpdateEvent) -> Void)? var subscription: EventSubscription? + var didRunSetup = false + var appliedOptions: UntoldViewOptions? + + /// Applies only the properties that differ from the last applied + /// options, so unrelated SwiftUI re-evaluations never touch the view. + func apply(_ options: UntoldViewOptions, to view: MTKView) { + let previous = appliedOptions + guard previous != options else { return } + + if previous?.preferredFramesPerSecond != options.preferredFramesPerSecond { + view.preferredFramesPerSecond = options.preferredFramesPerSecond + } + if previous?.isPaused != options.isPaused { + view.isPaused = options.isPaused + } + if previous?.clearColor != options.clearColor { + let c = options.clearColor + view.clearColor = MTLClearColor( + red: Double(c.x), green: Double(c.y), blue: Double(c.z), alpha: Double(c.w) + ) + } + appliedOptions = options + } } public func makeCoordinator() -> Coordinator { Coordinator() } + /// Resolves the stable renderer: adopts the injected one on the first + /// call, creates a fallback otherwise, and never swaps it afterwards — + /// SwiftUI may re-init this struct freely without recreating anything. + @MainActor + private func resolveRenderer(_ coordinator: Coordinator) -> UntoldRenderer? { + if coordinator.renderer == nil { + coordinator.renderer = renderer ?? UntoldRenderer.create() + } + return coordinator.renderer + } + + /// Runs the setup block exactly once, after the renderer exists so the + /// Metal device is available to resource-loading calls inside it. + @MainActor + private func runSetupIfNeeded(_ coordinator: Coordinator) { + guard !coordinator.didRunSetup else { return } + coordinator.didRunSetup = true + setupHandler?() + } + + @MainActor private func connect(_ coordinator: Coordinator) { coordinator.handler = updateHandler - guard coordinator.subscription == nil, updateHandler != nil, let renderer else { return } + guard coordinator.subscription == nil, updateHandler != nil, + let renderer = coordinator.renderer else { return } coordinator.subscription = renderer.onUpdate { [weak coordinator] event in // The MTKView delegate draws on the main thread; trap loudly if a // future host ever drives this renderer off-main. @@ -52,44 +106,65 @@ public struct SceneView: ViewRepresentable { } } + @MainActor + private func makeView(context: Context) -> MTKView { + let view = resolveRenderer(context.coordinator)?.metalView ?? MTKView() + runSetupIfNeeded(context.coordinator) + connect(context.coordinator) + context.coordinator.apply(options, to: view) + return view + } + + @MainActor + private func updateView(_ view: MTKView, context: Context) { + connect(context.coordinator) + context.coordinator.apply(options, to: view) + } + + @MainActor + private static func dismantleView(coordinator: Coordinator) { + coordinator.subscription?.cancel() + coordinator.subscription = nil + coordinator.handler = nil + coordinator.appliedOptions = nil + } + #if os(macOS) public func makeNSView(context: Context) -> MTKView { - connect(context.coordinator) - return mtkView + makeView(context: context) } - public func updateNSView(_: MTKView, context: Context) { - connect(context.coordinator) - updateView(mtkView, context: context) + public func updateNSView(_ view: MTKView, context: Context) { + updateView(view, context: context) } public static func dismantleNSView(_: MTKView, coordinator: Coordinator) { - coordinator.subscription?.cancel() - coordinator.subscription = nil - coordinator.handler = nil + dismantleView(coordinator: coordinator) } #else public func makeUIView(context: Context) -> MTKView { - connect(context.coordinator) - return mtkView + makeView(context: context) } - public func updateUIView(_ mtkView: MTKView, context: Context) { - connect(context.coordinator) - updateView(mtkView, context: context) + public func updateUIView(_ view: MTKView, context: Context) { + updateView(view, context: context) } public static func dismantleUIView(_: MTKView, coordinator: Coordinator) { - coordinator.subscription?.cancel() - coordinator.subscription = nil - coordinator.handler = nil + dismantleView(coordinator: coordinator) } #endif - public func updateView(_: MTKView, context _: Context) {} - - public func onInit(block: @escaping () -> Void) -> Self { - block() - return self + /// Registers a block that runs exactly once, when the platform view is + /// created and the renderer is ready. Use it for imperative scene setup + /// (loading meshes, creating entities). + /// + /// - Note: The block used to run immediately at body-evaluation time, on + /// every SwiftUI re-evaluation. It is now deferred until the renderer + /// exists and runs a single time for the lifetime of the view. + public func onInit(block: @escaping @MainActor () -> Void) -> Self { + var copy = self + copy.setupHandler = block + return copy } } diff --git a/Sources/UntoldEngine/Scenes/UntoldViewOptions.swift b/Sources/UntoldEngine/Scenes/UntoldViewOptions.swift new file mode 100644 index 000000000..327eac0e0 --- /dev/null +++ b/Sources/UntoldEngine/Scenes/UntoldViewOptions.swift @@ -0,0 +1,47 @@ +// +// UntoldViewOptions.swift +// UntoldEngine +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import simd + +/// Runtime-tunable settings for the SwiftUI host view. +/// +/// Unlike `UntoldRendererConfig` — which is create-time, immutable renderer +/// configuration — these values may change while the view is alive. When +/// SwiftUI re-evaluates the view with new options, only the properties that +/// actually changed are applied to the live `MTKView`; the renderer is never +/// recreated. Every property in this struct must be applicable to the live +/// view — anything that requires rebuilding the render pipeline belongs in +/// `UntoldRendererConfig` instead, and anything that tunes the engine itself +/// (anti-aliasing, post-FX, LOD, ...) already has a live channel through the +/// engine settings API (`setRendering`, `setPostFX`, `setLOD`, ...). +public struct UntoldViewOptions: Equatable, Sendable { + /// Target frame rate, applied to `MTKView.preferredFramesPerSecond`. + public var preferredFramesPerSecond: Int + + /// Pauses the draw loop (`MTKView.isPaused`). Simulation and rendering + /// stop and the last frame stays on screen. Use for menus, inactive + /// tabs, or battery saving. + public var isPaused: Bool + + /// Clear color of the drawable, linear RGBA. + public var clearColor: simd_float4 + + public init( + preferredFramesPerSecond: Int = 60, + isPaused: Bool = false, + clearColor: simd_float4 = simd_float4(0, 0, 0, 1) + ) { + self.preferredFramesPerSecond = preferredFramesPerSecond + self.isPaused = isPaused + self.clearColor = clearColor + } + + public static let `default` = UntoldViewOptions() +} diff --git a/Tests/UntoldEngineTests/UntoldViewOptionsTests.swift b/Tests/UntoldEngineTests/UntoldViewOptionsTests.swift new file mode 100644 index 000000000..5170ffd3b --- /dev/null +++ b/Tests/UntoldEngineTests/UntoldViewOptionsTests.swift @@ -0,0 +1,108 @@ +// +// UntoldViewOptionsTests.swift +// UntoldEngineTests +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import MetalKit +import simd +@testable import UntoldEngine +import XCTest + +/// Covers the runtime view options: struct semantics, the UntoldView +/// modifiers, and the coordinator-side diffing that applies changes to a +/// live MTKView. None of this needs a Metal device. +@MainActor +final class UntoldViewOptionsTests: XCTestCase { + // MARK: - Struct semantics + + func testDefaults() { + let options = UntoldViewOptions.default + + XCTAssertEqual(options.preferredFramesPerSecond, 60) + XCTAssertFalse(options.isPaused) + XCTAssertEqual(options.clearColor, simd_float4(0, 0, 0, 1)) + } + + func testEquality() { + XCTAssertEqual(UntoldViewOptions(), UntoldViewOptions.default) + XCTAssertNotEqual(UntoldViewOptions(preferredFramesPerSecond: 30), UntoldViewOptions.default) + XCTAssertNotEqual(UntoldViewOptions(isPaused: true), UntoldViewOptions.default) + XCTAssertNotEqual(UntoldViewOptions(clearColor: simd_float4(1, 0, 0, 1)), UntoldViewOptions.default) + } + + // MARK: - UntoldView modifiers + + func testUntoldViewModifiersUpdateOptions() { + let view = UntoldView(renderer: nil) {} + + XCTAssertEqual(view.options, .default) + XCTAssertEqual(view.preferredFramesPerSecond(30).options.preferredFramesPerSecond, 30) + XCTAssertTrue(view.paused(true).options.isPaused) + + let replaced = view.options(UntoldViewOptions(preferredFramesPerSecond: 120, isPaused: true)) + XCTAssertEqual(replaced.options.preferredFramesPerSecond, 120) + XCTAssertTrue(replaced.options.isPaused) + } + + func testUntoldViewModifiersDoNotMutateOriginal() { + let view = UntoldView(renderer: nil) {} + _ = view.preferredFramesPerSecond(30) + + XCTAssertEqual(view.options.preferredFramesPerSecond, 60) + } + + // MARK: - Coordinator apply diffing + + func testFirstApplySetsEveryProperty() { + let coordinator = SceneView.Coordinator() + let view = MTKView() + let options = UntoldViewOptions( + preferredFramesPerSecond: 30, + isPaused: true, + clearColor: simd_float4(0.5, 0.25, 0, 1) + ) + + coordinator.apply(options, to: view) + + XCTAssertEqual(view.preferredFramesPerSecond, 30) + XCTAssertTrue(view.isPaused) + XCTAssertEqual(view.clearColor.red, 0.5, accuracy: 1e-6) + XCTAssertEqual(view.clearColor.green, 0.25, accuracy: 1e-6) + XCTAssertEqual(coordinator.appliedOptions, options) + } + + func testApplyOnlyTouchesChangedProperties() { + let coordinator = SceneView.Coordinator() + let view = MTKView() + coordinator.apply(.default, to: view) + + // Perturb the view behind the coordinator's back: if apply diffs + // correctly, an FPS-only change must leave these values alone. + view.isPaused = true + view.clearColor = MTLClearColor(red: 0.9, green: 0, blue: 0, alpha: 1) + + var options = UntoldViewOptions.default + options.preferredFramesPerSecond = 120 + coordinator.apply(options, to: view) + + XCTAssertEqual(view.preferredFramesPerSecond, 120) + XCTAssertTrue(view.isPaused, "Unchanged isPaused option must not be re-applied") + XCTAssertEqual(view.clearColor.red, 0.9, accuracy: 1e-6, "Unchanged clearColor option must not be re-applied") + } + + func testApplyWithEqualOptionsIsANoOp() { + let coordinator = SceneView.Coordinator() + let view = MTKView() + coordinator.apply(.default, to: view) + + view.preferredFramesPerSecond = 15 + coordinator.apply(.default, to: view) + + XCTAssertEqual(view.preferredFramesPerSecond, 15, "Equal options must not touch the view at all") + } +} diff --git a/docs/proposals/UntoldViewOptions.md b/docs/proposals/UntoldViewOptions.md new file mode 100644 index 000000000..bfa7e58c0 --- /dev/null +++ b/docs/proposals/UntoldViewOptions.md @@ -0,0 +1,321 @@ +# Proposal: `UntoldViewOptions` — runtime-tunable view settings for SwiftUI + +**Status:** Draft +**Scope:** `UntoldView` / `SceneView` only. No changes to `UntoldRendererConfig` semantics, no `Observable` conformance on the renderer. + +## Motivation + +`UntoldRendererConfig` is create-time, immutable configuration (Metal view, pipeline init +blocks, render-system callbacks). That contract is correct and stays as-is: if those change, +recreating the renderer is acceptable. + +What is missing is a home for values that must change **while the view is alive** — target +FPS being the first concrete case. Exposing a mutable property on `UntoldView` itself does +not work: `UntoldView` is a SwiftUI value struct, so any state change that flows through it +re-evaluates the body, and today that also re-runs renderer creation (see "Prerequisite" +below). Making `UntoldRenderer` observable is deliberately rejected — it would couple the +engine to SwiftUI and is heavyweight for what is a handful of scalar settings. + +Agreed direction: + +- `UntoldRendererConfig` — immutable, renderer creation only. +- `UntoldViewOptions` — small `Equatable` value struct for runtime, view-host-scoped + settings. SwiftUI re-evaluates the view freely; we diff the options in + `updateNSView`/`updateUIView` against the last-applied copy stored in the coordinator and + touch the live `MTKView` only when something actually changed. + +## Prerequisite: stable renderer ownership + +Today both fallbacks re-create the renderer on every SwiftUI re-evaluation: + +```swift +// UntoldView.init +self.renderer = renderer ?? UntoldRenderer.create() // runs on every re-init of the struct + +// SceneView.init +self.renderer = renderer ?? UntoldRenderer.create() +``` + +The demos dodge this by creating the renderer outside and passing it in, but the API should +be safe by default. The fix is the same pattern already used for the `.onUpdate` +subscription: move fallback creation into the `Coordinator`, which SwiftUI keeps alive +across re-evaluations of the representable. + +- `SceneView` stores the *optional* renderer handed to it; it no longer force-creates one + in `init`. +- `Coordinator` gains `var renderer: UntoldRenderer?`. On first `makeNSView`/`makeUIView`, + it adopts the passed-in renderer or creates one, and from then on always returns the same + `MTKView`. +- `UntoldView` keeps accepting an optional external renderer (existing API unchanged) but + stops calling `UntoldRenderer.create()` in `init`; it just forwards the optional down. + +This is what makes the rest of the proposal sound: once the `MTKView`/renderer identity is +stable, applying option diffs to it is trivial. + +## API + +### The options struct + +```swift +/// Runtime-tunable settings for the SwiftUI host view. Unlike +/// `UntoldRendererConfig`, these may change while the view is alive; the view +/// applies the difference to the live `MTKView` without recreating the +/// renderer. Equatable so updates are no-ops when nothing changed. +public struct UntoldViewOptions: Equatable, Sendable { + /// Target frame rate, applied to `MTKView.preferredFramesPerSecond`. + public var preferredFramesPerSecond: Int + + /// Pauses the draw loop (`MTKView.isPaused`). Simulation and rendering + /// stop; the last frame stays on screen. Use for menus, inactive tabs, + /// or battery saving. + public var isPaused: Bool + + /// Clear color of the drawable, linear RGBA. + public var clearColor: simd_float4 + + public init( + preferredFramesPerSecond: Int = 60, + isPaused: Bool = false, + clearColor: simd_float4 = simd_float4(0, 0, 0, 1) + ) { + self.preferredFramesPerSecond = preferredFramesPerSecond + self.isPaused = isPaused + self.clearColor = clearColor + } + + public static let `default` = UntoldViewOptions() +} +``` + +### Surface on `UntoldView` + +Two equivalent ways to drive it — an init parameter for "bind the whole struct", and +per-property modifiers (same copy-on-write pattern `.onUpdate` already uses) for the common +one-liner: + +```swift +public init( + renderer: UntoldRenderer? = nil, + options: UntoldViewOptions = .default, + @SceneBuilder _ content: @escaping @MainActor () -> [any NodeProtocol] +) + +/// Replace all runtime options. +public func options(_ options: UntoldViewOptions) -> UntoldView + +/// Sugar for the common case. +public func preferredFramesPerSecond(_ fps: Int) -> UntoldView +public func paused(_ paused: Bool) -> UntoldView +``` + +Usage in an app: + +```swift +struct GameScreen: View { + @State private var fps = 60 + @State private var inMenu = false + + var body: some View { + UntoldView(renderer: renderer) { + MeshNode(resource: "redplayer.usdz", entityID: rootID) + } + .preferredFramesPerSecond(fps) + .paused(inMenu) + + Picker("FPS", selection: $fps) { + Text("30").tag(30); Text("60").tag(60); Text("120").tag(120) + } + } +} +``` + +When `fps` changes, SwiftUI re-evaluates the body, `updateNSView`/`updateUIView` runs, the +coordinator sees `appliedOptions != options`, and only +`mtkView.preferredFramesPerSecond` is touched. The renderer, the `MTKView`, the frame-event +subscription, and the scene are untouched. + +## Implementation + +### `SceneView` changes + +```swift +public struct SceneView: ViewRepresentable { + private var renderer: UntoldRenderer? // optional now; no create() in init + private var options: UntoldViewOptions + private var updateHandler: (@MainActor (UpdateEvent) -> Void)? + + public init( + renderer: UntoldRenderer? = nil, + options: UntoldViewOptions = .default, + updateHandler: (@MainActor (UpdateEvent) -> Void)? = nil + ) { + self.renderer = renderer + self.options = options + self.updateHandler = updateHandler + } + + @MainActor + public final class Coordinator { + var renderer: UntoldRenderer? + var handler: (@MainActor (UpdateEvent) -> Void)? + var subscription: EventSubscription? + /// Last options actually applied to the MTKView; nil until first apply. + var appliedOptions: UntoldViewOptions? + } + + public func makeCoordinator() -> Coordinator { Coordinator() } + + /// Resolves the stable renderer: adopts the injected one on first call, + /// creates a fallback otherwise, and never swaps it afterwards. + private func resolveRenderer(_ coordinator: Coordinator) -> UntoldRenderer? { + if coordinator.renderer == nil { + coordinator.renderer = renderer ?? UntoldRenderer.create() + } + return coordinator.renderer + } + + private func connect(_ coordinator: Coordinator) { + coordinator.handler = updateHandler + guard coordinator.subscription == nil, updateHandler != nil, + let renderer = coordinator.renderer else { return } + coordinator.subscription = renderer.onUpdate { [weak coordinator] event in + MainActor.assumeIsolated { + coordinator?.handler?(event) + } + } + } + + /// Diffs against the last-applied options and touches the MTKView only + /// for properties that changed. + private func apply(_ options: UntoldViewOptions, to view: MTKView, coordinator: Coordinator) { + let previous = coordinator.appliedOptions + guard previous != options else { return } + + if previous?.preferredFramesPerSecond != options.preferredFramesPerSecond { + view.preferredFramesPerSecond = options.preferredFramesPerSecond + } + if previous?.isPaused != options.isPaused { + view.isPaused = options.isPaused + } + if previous?.clearColor != options.clearColor { + let c = options.clearColor + view.clearColor = MTLClearColor(red: Double(c.x), green: Double(c.y), + blue: Double(c.z), alpha: Double(c.w)) + } + coordinator.appliedOptions = options + } + + #if os(macOS) + public func makeNSView(context: Context) -> MTKView { + let view = resolveRenderer(context.coordinator)?.metalView ?? MTKView() + connect(context.coordinator) + apply(options, to: view, coordinator: context.coordinator) + return view + } + + public func updateNSView(_ view: MTKView, context: Context) { + connect(context.coordinator) + apply(options, to: view, coordinator: context.coordinator) + } + #else + // identical bodies for makeUIView/updateUIView + #endif +} +``` + +`dismantleNSView`/`dismantleUIView` additionally clear `coordinator.appliedOptions` (and +`coordinator.renderer` if we decide the coordinator owns fallback renderers' lifetime). + +### `UntoldView` changes + +```swift +public struct UntoldView: View { + private var renderer: UntoldRenderer? // no create() in init anymore + private var options: UntoldViewOptions + private var content: [any NodeProtocol] = [] + private var updateHandler: (@MainActor (UpdateEvent) -> Void)? + + public init( + renderer: UntoldRenderer? = nil, + options: UntoldViewOptions = .default, + @SceneBuilder _ content: @escaping @MainActor () -> [any NodeProtocol] + ) { + self.renderer = renderer + self.options = options + self.content = content() + } + + public var body: some View { + SceneView(renderer: renderer, options: options, updateHandler: updateHandler) + } + + public func options(_ options: UntoldViewOptions) -> UntoldView { + var copy = self; copy.options = options; return copy + } + + public func preferredFramesPerSecond(_ fps: Int) -> UntoldView { + var copy = self; copy.options.preferredFramesPerSecond = fps; return copy + } + + public func paused(_ paused: Bool) -> UntoldView { + var copy = self; copy.options.isPaused = paused; return copy + } +} +``` + +Note the `@State private var metalView` currently held by `UntoldView` goes away — the +coordinator is the stable holder, and `UntoldView` never needs the view directly. + +## What belongs in options (and what does not) + +Rule of thumb: **`UntoldViewOptions` owns properties of the host `MTKView` / presentation +surface.** Anything that tunes the engine or render pipeline itself already has a live +settings channel (`setRendering`, `setPostFX`, `setLOD`, `setEngine`, … backed by the +lock-protected shared store) and stays there — those work from any context, not just +SwiftUI, and duplicating them here would create two sources of truth. + +Included now: + +| Property | Applied to | Why | +|---|---|---| +| `preferredFramesPerSecond` | `MTKView.preferredFramesPerSecond` | The motivating case. Also removes the hardcoded 60 in `UntoldRenderer.create()` as the only knob. | +| `isPaused` | `MTKView.isPaused` | Cheap, obviously view-scoped, immediately useful (menus, background, battery). | +| `clearColor` | `MTKView.clearColor` | Cosmetic surface property; lets SwiftUI theme the canvas. | + +Good candidates for a follow-up, same mechanism: + +- **`renderScale: Float`** (dynamic resolution) — scale the drawable size relative to the + view's native size. Flows naturally through the existing + `mtkView(_:drawableSizeWillChange:)` resize path, but needs care because `create()` + currently pins `contentsScale = 1.0`; worth its own pass. +- **`drawsOnDemand: Bool`** — `enableSetNeedsDisplay` + paused combo for editor-style + "render only when dirty" hosts. + +Deliberately excluded: + +- Anti-aliasing, post-FX, LOD, debug views, environment — already live-tunable via + `EngineSettingsAPI`; call those from `onChange` in SwiftUI if needed. +- `gameMode` — engine-wide state, not a property of one host view. +- Anything from `UntoldRendererConfig` — pipeline blocks and callbacks stay create-time. + +## Behavior notes + +- **No renderer recreation, ever, from options.** Every property in the struct must be + applicable to the live `MTKView`. If a future setting cannot be applied live, it belongs + in `UntoldRendererConfig`, not here — that is the invariant that keeps the two types from + blurring together. +- **Idempotent application.** The coordinator-side diff means a re-evaluation storm from + unrelated SwiftUI state costs one `Equatable` compare and nothing else. +- **Multiple views** — options are per-`SceneView` instance (per coordinator), so two + `UntoldView`s over different renderers can run different FPS/pause states independently. +- `Sendable` is free (scalars + simd), which keeps the struct usable from non-main contexts + that compute settings, even though application always happens on the main thread inside + the representable callbacks. + +## Migration + +- `UntoldView(renderer:)` and `SceneView(renderer:)` signatures gain only defaulted + parameters — source-compatible. +- The SceneBuilder demo (UntoldArcade) can drop its "create renderer in the parent's init" + workaround once the coordinator owns the fallback, though passing an external renderer + remains supported and correct.