Skip to content

Instantly share code, notes, and snippets.

View banjun's full-sized avatar

banjun banjun

View GitHub Profile
@banjun
banjun / RenderTextureScene.swift
Created February 9, 2025 16:19
RealityKit wrapper for RenderTexture like scene, with supports for multiple cameras
import Foundation
import RealityFoundation
///
/// ```swift
/// renderTextureScene.entities.append(rootEntity.clone(recursive: true))
/// let renderTextureMaterial = UnlitMaterial(texture: renderTextureScene.textureResources[0])
/// ModelEntity(mesh: .generatePlane(width: 1, height: 1), materials: [renderTextureMaterial])
/// ...
///
@banjun
banjun / SwiftUIObservationLeaks.swift
Created February 4, 2025 04:27
SwiftUI & Observation memory leaks
import SwiftUI
import Observation
// Leaks Malloc 48 Bytes block, 144 Bytes block, 128 Bytes block on macOS 15.3 (24D60), Xcode 16.2 (16C5032a)
// leaks speed is ~ +10MB / 5 mins, with Instruments
@main
struct SwiftUIObservationLeaksApp: App {
@State private var model: Model = .init()
@MainActor @Observable final class Model {
extension ModelEntity {
/// create example model with skeleton: https://openusd.org/dev/api/_usd_skel__schema_overview.html
static func createUsdSkelExample() async throws -> ModelEntity {
var d = MeshDescriptor()
d.positions = .init([
.init(0.5, -0.5, 4), .init(-0.5, -0.5, 4), .init(0.5, 0.5, 4), .init(-0.5, 0.5, 4),
.init(-0.5, -0.5, 0), .init(0.5, -0.5, 0), .init(-0.5, 0.5, 0), .init(0.5, 0.5, 0),
.init(-0.5, 0.5, 2), .init(0.5, 0.5, 2), .init(0.5, -0.5, 2), .init(-0.5, -0.5, 2)])
d.primitives = .trianglesAndQuads(triangles: [], quads: [
2, 3, 1, 0,
import SwiftUI
import RealityKit
@main
struct KibouApp: App {
@Environment(\.scenePhase) private var scenePhase
@State private var transform: Transform = .identity
@GestureState private var dragState: SIMD3<Float>?
var body: some SwiftUI.Scene {
@banjun
banjun / TimerOnMainActor.swift
Last active September 13, 2024 09:09
Timer.scheduledTimer on MainActor
import Foundation
extension Timer {
@MainActor @discardableResult static func scheduledTimerOnMainRunLoop(withTimeInterval: TimeInterval, repeats: Bool, block: @escaping @MainActor (Timer) -> Void) -> Timer {
// The doc says: Creates and returns a new NSTimer object initialized with the specified block object and schedules it on the current run loop
// But the block is not annotated with @MainActor and we need MainActor.assumeIsolated
// Since Timer is non-Sendable, capture by nonisolated(unsafe) to pass from nonisolated context to MainActor.
Timer.scheduledTimer(withTimeInterval: withTimeInterval, repeats: repeats) { timer in
nonisolated(unsafe) let timer: Timer = timer
MainActor.assumeIsolated {
@banjun
banjun / generatePlaneMesh.swift
Last active August 2, 2024 09:55
Create Plane USDA
import simd
func generatePlaneMesh(lengthInMeter: Float = 1, divisions: Int = 100, name: String = "GeneratedPlane") {
let n = divisions
let xys: [(Int, Int)] = (0..<n).flatMap { y in (0..<n).map { x in (x, y) }}
let points: [SIMD3<Float>] = xys.map { x, y in
SIMD3(Float(x) / Float(n - 1) - 0.5, Float(y) / Float(n - 1) - 0.5, 0) * lengthInMeter
}
let faceVertexIndices: [Int] = xys.flatMap { x, y in
guard x < n - 1, y < n - 1 else { return [Int]() }
@banjun
banjun / MultiColumnText.swift
Last active May 2, 2024 07:53
SwiftUI multi column text backed by TextKit 1 multi column layout
struct MultiColumnText: View {
private let storage: NSTextStorage
private let layoutManager: NSLayoutManager = .init()
private let containers: [NSTextContainer]
init(text: NSAttributedString, columns: Int) {
storage = .init(attributedString: text)
storage.addLayoutManager(layoutManager)
containers = (0..<columns).map {_ in .init()}
containers.forEach {
@banjun
banjun / Inwardbox.swift
Created April 16, 2024 05:19
for visionOS gestures, generate a box with its surfaces facing inward
import RealityKit
extension ShapeResource {
/// generate a box with its surfaces facing inward.
/// NOTE: size 30 works with interactions, 50 does not work with interactions
static func generateInwardBox(size: Float) async throws -> ShapeResource {
// An array of vertex positions containing discrete points on the mesh.
let meshPositions: [SIMD3<Float>] = [
.init(-1, -1, -1), // 0 LBF
.init(+1, -1, -1), // 1 RBF
@banjun
banjun / UserActivityWindowGroup.swift
Last active March 19, 2024 16:49
Open new window on drop UserActivity
import SwiftUI
// prerequisites in Info.plist: NSUserActivityTypes contains type, UIApplicationSceneManifest/UIApplicationSupportsMultipleScenes = YES
// accepts NSUserActivity.targetContentIdentifier = type
// see also: https://developer.apple.com/documentation/swiftui/scene/handlesexternalevents(matching:)
struct UserActivityWindowGroup<Content: View, Payload: Codable>: Scene {
var type: String
@ViewBuilder var content: (Payload) -> Content
init(type: String, payloadType: Payload.Type, @ViewBuilder content: @escaping (Payload) -> Content) {
@banjun
banjun / convert-lib-fw-to-sim-device-universal-xcfw.sh
Last active October 30, 2023 07:28
create an xcframework from a universal static lib .a or universal static .framework without rebuild
#!/bin/zsh
if [[ -z $BOGO_ARM64_TO_SIM ]]; then
echo "❌ requires BOGO_ARM64_TO_SIM: set an executable binary path built from https://github.com/bogo/arm64-to-sim"
exit 1;
fi
set -eu
# set -x # debug log