Post

Replies

Boosts

Views

Activity

Reply to version update in Vision Pro
Hi thanks for the reply! the problem I'm having is with the alignment of a model. I tap 2 times on a 3D model and then 2 times on a grid of spheres. after calculating the alignment the model should jump to it's new position on the grid of spheres and match the target points. I have an unknown visual offset after the alignment even though visually I can see the taps are in the correct place. I'll add a snippet of my alignment function and an image of the visual. the brute force offset you can see in the code was to try and move the alignment a meter to the left (which wasn't successful). func alignModel2Points() { guard let modelPointA = modelPointA, let modelPointB = modelPointB, let targetPointA = targetPointA, let targetPointB = targetPointB, let modelRootEntity = modelRootEntity, let modelAnchor = modelAnchor else { print("❌ Missing data for alignment") return } let offset = SIMD3(-1, 0, 0) let modelPointA_shifted = modelPointA + offset let modelPointB_shifted = modelPointB + offset let targetPointA_shifted = targetPointA + offset let targetPointB_shifted = targetPointB + offset modelRootEntity.position = .zero modelRootEntity.orientation = simd_quatf() modelRootEntity.scale = SIMD3<Float>(repeating: 1.0) pivotEntity?.position = .zero // 1) compute scale from the two segments let modelVec = modelPointB_shifted - modelPointA_shifted let targetVec = targetPointB_shifted - targetPointA_shifted let modelLen = length(modelVec) let targetLen = length(targetVec) guard modelLen > 1e-6 else { print("❌ model points coincide") return } let s = targetLen / modelLen // 2) compute rotation that aligns modelVec -> targetVec let modelDir = normalize(modelVec) let targetDir = normalize(targetVec) let crossProd = cross(modelDir, targetDir) let crossLen = length(crossProd) var rot: simd_quatf if crossLen < 1e-6 { // parallel or opposite if dot(modelDir, targetDir) > 0 { rot = simd_quatf(angle: 0, axis: [0,1,0]) } else { var tmp = cross(modelDir, [0,1,0]) if length(tmp) < 1e-6 { tmp = cross(modelDir, [1,0,0]) } rot = simd_quatf(angle: .pi, axis: normalize(tmp)) } } else { let dp = dot(modelDir, targetDir) let clamped = min(max(dp, -1.0), 1.0) let angle = acos(clamped) rot = simd_quatf(angle: angle, axis: normalize(crossProd)) } // 3) Compute world-space translation for the anchor: // We want: worldPoint = AnchorTransform * (rot * (s * modelPoint) + modelRootLocalPosition) // Since modelRootEntity is identity at origin, simplified: // translation = targetPointA - rot.act(modelPointA_shifted * s) let transformedModelA = rot.act(modelPointA_shifted * s) let translation = targetPointA - transformedModelA // 4) Build final Transform and set it on the modelAnchor (anchor is root — transform is world) var final = Transform() final.scale = SIMD3<Float>(repeating: s) final.rotation = rot final.translation = translation // Apply final transform to the anchor (this places the whole model in world space) modelAnchor.transform = final alignedModelPosition = modelAnchor.position alignmentDone = true // debug prints let modelA_world_after = modelRootEntity.convert(position: modelPointA_shifted, to: nil) let modelB_world_after = modelRootEntity.convert(position: modelPointB_shifted, to: nil) print(""" ✅ ALIGN: scale s = \(s) rotation = \(rot) translation = \(translation) modelA_world_after = \(modelA_world_after) targetA = \(targetPointA) modelB_world_after = \(modelB_world_after) targetB = \(targetPointB) finalAnchor = \(modelAnchor.position) """) // cleanup // removeAllMarkers() NotificationCenter.default.post(name: .alignmentDidComplete, object: nil) debugSpawnSpheres() printAlignmentOffsets() } func printAlignmentOffsets() { guard let mr = modelRootEntity, let mA = modelPointA, let mB = modelPointB, let tA = targetPointA, let tB = targetPointB else { return } let mA_world = mr.convert(position: mA, to: nil) let mB_world = mr.convert(position: mB, to: nil) let offsetA = tA - mA_world let offsetB = tB - mB_world print("DEBUG OFFSETS:") print(" mA_world = \(mA_world)") print(" tA = \(tA)") print(" offsetA = \(offsetA)") print(" mB_world = \(mB_world)") print(" tB = \(tB)") print(" offsetB = \(offsetB)") } func debugSpawnSpheres() { guard let c = content, let a = modelRootEntity, let A = modelPointA, let B = modelPointB, let tA = targetPointA, let tB = targetPointB else { return } let s = [(a.convert(position: A, to: nil), UIColor.red), (a.convert(position: B, to: nil), UIColor.orange), (tA, UIColor.green), (tB, UIColor.blue)] for (pos, color) in s { let e = ModelEntity(mesh: MeshResource.generateSphere(radius: 0.01), materials: [SimpleMaterial(color: color, isMetallic: false)]) e.position = pos c.add(e) allMarkers.append(e) } }
Oct ’25