PhoneDepthCloud
A live rear-LiDAR RGBD cloud from Ollin Capture (tap World). This is the world-facing depth feed from Ollin's own app, unprojected with the stream's true intrinsics and orbited. Needs import OllinPhone.
- Run
swift run --package-path Examples Example-3D-Phone-PhoneDepthCloud- Imports
OllinPhone- Source
- Examples/3D/Phone/PhoneDepthCloud/Sketch.swift
import Foundation
import simd
import Ollin
import OllinPhone
/// A **live** world-facing point cloud streamed from a tethered iPhone's rear LiDAR
/// through the Ollin Capture app — the depth sibling of `PhoneBodyPose`/`PhoneFace`.
/// Point the phone at the room and the cloud is the room.
///
/// Setup: install **Ollin Capture** on a LiDAR iPhone (a Pro model), launch it, tap
/// the **World** segment, and connect the cable. The connection retries on its own,
/// so tapping World (or plugging in) after this sketch is already running just begins
/// the feed.
///
/// Each frame's color image and metric depth map unproject with the stream's true
/// camera intrinsics into a colored cloud, framed and spun so the depth reads as real
/// space. The transport is the standard usbmuxd tunnel; the wire format is Ollin's own.
@main
final class PhoneDepthCloud: Sketch {
let device = PhoneDevice()
// The orbit framing, eased frame-to-frame so live sensor noise doesn't jitter it.
var orbitCenter: Vector3?
var orbitRadius = 0.0
override func setup() {
device.start()
}
override func draw() {
background(Color(white: 0.04))
// The rear LiDAR depth map is a fixed 256×192 (~49k samples) — ARKit's ceiling
// for scene depth — so density comes from keeping samples, not adding them: a
// `.low` confidence floor keeps the dimmer/farther points `.medium` would drop,
// and a slightly larger splat reads as a solid surface rather than dots.
guard let pose = device.latestPose,
let cameraCloud = device.pointCloud(minConfidence: .low,
depthRange: 0.3...5.0, pointSize: 0.014),
!cameraCloud.isEmpty else {
return drawStatus(device.waitingMessage + "\n\n" +
"Open Ollin Capture on a LiDAR iPhone, tap the World tab,\n" +
"and connect the cable.", style: .info)
}
// The LiDAR depth arrives in the camera's native (landscape) frame, so a
// portrait-held phone yields a cloud rolled 90°. Re-orient each point with the
// camera pose's rotation (the w = 0 multiply drops the translation, so the
// cloud stays centered near the origin) — ARKit's world is gravity-aligned, so
// this stands the scene upright however the phone is held.
var cloud = PointCloud()
cloud.points.reserveCapacity(cameraCloud.count)
for p in cameraCloud.points {
let w = pose * SIMD4<Float>(Float(p.position.x), Float(p.position.y), Float(p.position.z), 0)
cloud.add(Vector3(Double(w.x), Double(w.y), Double(w.z)), color: p.color, size: p.size)
}
// Frame the cloud: orbit its centroid at a radius set by how spread out it is.
var sum = Vector3.zero
for p in cloud.points { sum += p.position }
let center = sum / Double(cloud.count)
var spread = 0.0
for p in cloud.points { spread += p.position.distanceSquared(to: center) }
let radius = max(0.6, (spread / Double(cloud.count)).squareRoot() * 3)
// Live depth flickers frame to frame (sensor noise, points crossing the
// confidence/range filters); ease the framing toward it so the cloud holds
// steady while real motion is still followed.
if let c = orbitCenter {
orbitCenter = c.lerp(to: center, 0.08)
orbitRadius += (radius - orbitRadius) * 0.08
} else {
orbitCenter = center
orbitRadius = radius
}
cameraShowcase(.turntable(period: .tau / 0.3), target: orbitCenter ?? center, radius: orbitRadius,
elevation: 0.18, fieldOfView: .pi / 3)
drawPointCloud(cloud)
if let frame = device.latestFrame {
drawCaption("PhoneDepthCloud — \(frame.depthWidth)×\(frame.depthHeight) depth, " +
"\(cloud.count) pts; drag to spin")
}
}
}