Skip to content

Commit

Permalink
refactor: impl world tracking source feature + broken motion tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
kotleni committed Feb 23, 2024
1 parent 6ffb5ba commit d897cc0
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 53 deletions.
20 changes: 20 additions & 0 deletions ALVRClient.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
/* Begin PBXBuildFile section */
5A4229242B8761B600AEE1C2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5A4229232B8761B600AEE1C2 /* LaunchScreen.storyboard */; };
5A945A8B2B88A12100CA1254 /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A945A8A2B88A12100CA1254 /* Renderer.swift */; };
5A945A8D2B88BC1D00CA1254 /* WorldTrackingSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A945A8C2B88BC1D00CA1254 /* WorldTrackingSource.swift */; };
5A945A902B88BC7300CA1254 /* ARWorldTrackingSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A945A8F2B88BC7300CA1254 /* ARWorldTrackingSource.swift */; };
5A945A922B88BF1E00CA1254 /* MotionWorldTrackingSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A945A912B88BF1E00CA1254 /* MotionWorldTrackingSource.swift */; };
5AD6913D2B87B709002FB88C /* WorldTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD6913C2B87B709002FB88C /* WorldTracker.swift */; };
5AD6913F2B87BFF0002FB88C /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD6913E2B87BFF0002FB88C /* Main.swift */; };
5AD9D1942B889DC500FA24CE /* Int64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD9D1932B889DC500FA24CE /* Int64.swift */; };
Expand Down Expand Up @@ -38,6 +41,9 @@
/* Begin PBXFileReference section */
5A4229232B8761B600AEE1C2 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
5A945A8A2B88A12100CA1254 /* Renderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
5A945A8C2B88BC1D00CA1254 /* WorldTrackingSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorldTrackingSource.swift; sourceTree = "<group>"; };
5A945A8F2B88BC7300CA1254 /* ARWorldTrackingSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ARWorldTrackingSource.swift; sourceTree = "<group>"; };
5A945A912B88BF1E00CA1254 /* MotionWorldTrackingSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionWorldTrackingSource.swift; sourceTree = "<group>"; };
5AB8A52F2B861F9600C343FE /* ALVRClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ALVRClient.entitlements; sourceTree = "<group>"; };
5AD6913C2B87B709002FB88C /* WorldTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorldTracker.swift; sourceTree = "<group>"; };
5AD6913E2B87BFF0002FB88C /* Main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Main.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -68,6 +74,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
5A945A8E2B88BC6100CA1254 /* WorldTrackingSources */ = {
isa = PBXGroup;
children = (
5A945A8F2B88BC7300CA1254 /* ARWorldTrackingSource.swift */,
5A945A912B88BF1E00CA1254 /* MotionWorldTrackingSource.swift */,
);
path = WorldTrackingSources;
sourceTree = "<group>";
};
5AD9D1922B889D3900FA24CE /* Extensions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -98,9 +113,11 @@
C5E5902C2B6379ED00328ED6 /* ALVRClient */ = {
isa = PBXGroup;
children = (
5A945A8E2B88BC6100CA1254 /* WorldTrackingSources */,
5AD9D1922B889D3900FA24CE /* Extensions */,
C5E590312B6379ED00328ED6 /* ALVRClientApp.swift */,
5A945A8A2B88A12100CA1254 /* Renderer.swift */,
5A945A8C2B88BC1D00CA1254 /* WorldTrackingSource.swift */,
5AD6913C2B87B709002FB88C /* WorldTracker.swift */,
D61E6FDF2B71CE080076031A /* VideoHandler.swift */,
5AD6913E2B87BFF0002FB88C /* Main.swift */,
Expand Down Expand Up @@ -206,11 +223,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5A945A922B88BF1E00CA1254 /* MotionWorldTrackingSource.swift in Sources */,
5A945A8D2B88BC1D00CA1254 /* WorldTrackingSource.swift in Sources */,
C5E590382B6379ED00328ED6 /* Shaders.metal in Sources */,
5AD6913F2B87BFF0002FB88C /* Main.swift in Sources */,
5A945A8B2B88A12100CA1254 /* Renderer.swift in Sources */,
5AD9D1942B889DC500FA24CE /* Int64.swift in Sources */,
5AD6913D2B87B709002FB88C /* WorldTracker.swift in Sources */,
5A945A902B88BC7300CA1254 /* ARWorldTrackingSource.swift in Sources */,
D61E6FE02B71CE080076031A /* VideoHandler.swift in Sources */,
C5E590322B6379ED00328ED6 /* ALVRClientApp.swift in Sources */,
);
Expand Down
2 changes: 1 addition & 1 deletion ALVRClient/ALVRClientApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct MetalView: UIViewRepresentable {
private var lastBatteryStateUpdateTime: Int64 = 0

init(_ parent: MetalView) {
self.worldTracker = WorldTracker()
self.worldTracker = WorldTracker(trackingMode: .arSession)
self.parent = parent

guard let metalDevice = MTLCreateSystemDefaultDevice() else { fatalError("Can't create metal device.") }
Expand Down
73 changes: 21 additions & 52 deletions ALVRClient/WorldTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,80 +7,49 @@

import ARKit

final class WorldTracker: NSObject, ARSessionDelegate {
private let dispatchQueue: DispatchQueue
private let configuration: ARWorldTrackingConfiguration // or AROrientationTrackingConfiguration
private let arSession: ARSession
// ARSession have very big impact for battery
// Maybe i should use CoreMotion?
// But ARSession can track position in space

private var lastTickTime: Int64 = 0
private var tps = 0

// FIXME: Monkey code
private var linearVelocity: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)
private var position: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)
private var rotation: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)

override init() {
dispatchQueue = .init(label: "WorldTrackerQueue", qos: .background)

configuration = .init()
configuration.planeDetection = .horizontal

arSession = ARSession()

super.init()

// Start ar session
dispatchQueue.async { [weak self] in
guard let self = self else { return }
self.arSession.run(self.configuration)
}

arSession.delegate = self
final class WorldTracker {
enum WorldTrackingMode {
case arSession
case coreMotion // Not working yet
}

func session(_ session: ARSession, didUpdate frame: ARFrame) {
// TODO: linearVelocity
private let worldTrackingSource: WorldTrackingSource

init(trackingMode: WorldTrackingMode) {
print("World tracking mode: \(trackingMode)")

if let framePosition = arSession.currentFrame?.camera.transform.columns.3 {
// FIXME: Need to calibrate y offset
// One metter offset just matched for initial position on my desk
position = (framePosition.x, framePosition.y + 1.0 /* 1 metter offset */, framePosition.z)
let worldTrackingSource: WorldTrackingSource
if trackingMode == .arSession {
worldTrackingSource = ARWorldTrackingSource()
} else if trackingMode == .coreMotion {
worldTrackingSource = MotionWorldTrackingSource()
} else {
fatalError("Do you miss processing new tracking mode?")
}

if let frameEuler = arSession.currentFrame?.camera.eulerAngles {
rotation = (frameEuler.x, frameEuler.y, frameEuler.z)
}
worldTrackingSource.start()

tps += 1
if Int64.getCurrentMillis() - lastTickTime > 1000 {
lastTickTime = Int64.getCurrentMillis()
// print("World tracker tps is \(tps)")
tps = 0
}
self.worldTrackingSource = worldTrackingSource
}

/// Get device linear velocity
func getLinearVelocity() -> (Float, Float, Float) {
return linearVelocity
return worldTrackingSource.getLinearVelocity()
}

/// Get device position
func getPosition() -> (Float, Float, Float) {
return position
return worldTrackingSource.getPosition()
}

/// Get device euler rotation
func getRotation() -> (Float, Float, Float) {
return rotation
return worldTrackingSource.getRotation()
}

/// Get device quaterion rotation
func getQuaterionRotation() -> AlvrQuat {
let r = rotation
let r = getRotation()

// Get quaternion components
let cr = cos(r.0 * 0.5)
Expand Down
17 changes: 17 additions & 0 deletions ALVRClient/WorldTrackingSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// WorldTrackingSource.swift
// ALVRClient
//
// Created by Viktor Varenik on 23.02.2024.
//

import Foundation

protocol WorldTrackingSource {
func getLinearVelocity() -> (Float, Float, Float)
func getPosition() -> (Float, Float, Float)
func getRotation() -> (Float, Float, Float)

func start()
func stop()
}
83 changes: 83 additions & 0 deletions ALVRClient/WorldTrackingSources/ARWorldTrackingSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// ARWorldTrackingSource.swift
// ALVRClient
//
// Created by Viktor Varenik on 23.02.2024.
//

import ARKit

class ARWorldTrackingSource: NSObject, ARSessionDelegate, WorldTrackingSource {
private let dispatchQueue: DispatchQueue
private let configuration: ARWorldTrackingConfiguration // or AROrientationTrackingConfiguration
private let arSession: ARSession
// ARSession have very big impact for battery
// Maybe i should use CoreMotion?
// But ARSession can track position in space

private var lastTickTime: Int64 = 0
private var tps = 0

// FIXME: Monkey code
private var linearVelocity: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)
private var position: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)
private var rotation: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)

override init() {
dispatchQueue = .init(label: "ARWorldTrackingSource", qos: .background)

configuration = .init()
configuration.planeDetection = .horizontal

arSession = ARSession()

super.init()

arSession.delegate = self
}

func session(_ session: ARSession, didUpdate frame: ARFrame) {
// TODO: linearVelocity

if let framePosition = arSession.currentFrame?.camera.transform.columns.3 {
// FIXME: Need to calibrate y offset
// One metter offset just matched for initial position on my desk
position = (framePosition.x, framePosition.y + 1.0 /* 1 metter offset */, framePosition.z)
}

if let frameEuler = arSession.currentFrame?.camera.eulerAngles {
rotation = (frameEuler.x, frameEuler.y, frameEuler.z)
}

tps += 1
if Int64.getCurrentMillis() - lastTickTime > 1000 {
lastTickTime = Int64.getCurrentMillis()
// print("World tracker tps is \(tps)")
tps = 0
}
}

func start() {
// Start ar session
dispatchQueue.async { [weak self] in
guard let self = self else { return }
self.arSession.run(self.configuration)
}
}

func stop() {
arSession.pause()
}

func getLinearVelocity() -> (Float, Float, Float) {
return linearVelocity
}

func getPosition() -> (Float, Float, Float) {
return position
}

func getRotation() -> (Float, Float, Float) {
return rotation
}
}
60 changes: 60 additions & 0 deletions ALVRClient/WorldTrackingSources/MotionWorldTrackingSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// MotionWorldTrackingSource.swift
// ALVRClient
//
// Created by Viktor Varenik on 23.02.2024.
//

import CoreMotion

// FIXME: Not working yet!
class MotionWorldTrackingSource: NSObject, WorldTrackingSource {
private let dispatchQueue: DispatchQueue
private let motionManager: CMMotionManager

private var lastTickTime: Int64 = 0
private var tps = 0

// FIXME: Monkey code
private var linearVelocity: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)
private var position: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)
private var rotation: (Float, Float, Float) = (Float.zero, Float.zero, Float.zero)

override init() {
dispatchQueue = .init(label: "ARWorldTrackingSource", qos: .background)

motionManager = .init()
motionManager.deviceMotionUpdateInterval = 1000 / 30 // 30 tps

super.init()
}

func start() {
motionManager.startDeviceMotionUpdates(to: .main) { [weak self] motion, error in
if error != nil { return }
guard let self = self else { return }
guard let motion = motion else { return }

let attitude = motion.attitude
rotation = (Float(attitude.roll), Float(attitude.pitch), Float(attitude.yaw))

position.2 = 1.7
}
}

func stop() {
motionManager.stopDeviceMotionUpdates()
}

func getLinearVelocity() -> (Float, Float, Float) {
return linearVelocity
}

func getPosition() -> (Float, Float, Float) {
return position
}

func getRotation() -> (Float, Float, Float) {
return rotation
}
}

0 comments on commit d897cc0

Please sign in to comment.