version update in Vision Pro

Hi, I'm developing an app for Vision Pro using Xcode, while updating the latest update, things that worked in my app suddenly didn't. in my app flow I'm tapping spheres to get their positions, from some reason I get an offset from where I tap to where a marker on that position is showing up. here's the part of code that does that, and a part that is responsible for an alignment that happens afterwards:

func loadMainScene(at position: SIMD3<Float>) async { guard let content = self.content else { return }

    do {
        let rootEntity = try await Entity(named: "surgery 16.09", in: realityKitContentBundle)
        rootEntity.scale = SIMD3<Float>(repeating: 0.5)
        rootEntity.generateCollisionShapes(recursive: true)
        self.modelRootEntity = rootEntity
        let bounds = rootEntity.visualBounds(relativeTo: nil)
        print("📏 Model bounds: center=\(bounds.center), extents=\(bounds.extents)")
        let pivotEntity = Entity()
        pivotEntity.addChild(rootEntity)
        self.pivotEntity = pivotEntity
        let modelAnchor = AnchorEntity(world: [1, 1.3, -0.8])
        modelAnchor.addChild(pivotEntity)
        content.add(modelAnchor)
        updateModelOpacity(0.5)
        self.modelAnchor = modelAnchor
        rootEntity.visit { entity in
            print("👀 Entity in model: \(entity.name)")
            if entity.name.lowercased().hasPrefix("focus") {
                entity.generateCollisionShapes(recursive: true)
                entity.components.set(InputTargetComponent())
                print("🎯 Made tappable: \(entity.name)")
            }
        }
        print("✅ Model loaded with collisions")
        guard let sphere = placementSphere else { return }
        let sphereWorldXform = sphere.transformMatrix(relativeTo: nil)
        var newXform = sphereWorldXform
        newXform.columns.3.y += 0.1   // move up by 20 cm
        let gridAnchor = AnchorEntity(world: newXform)
        self.gridAnchor = gridAnchor
        content.add(gridAnchor)
        let baseScene = try await Entity(named: "Scene", in: realityKitContentBundle)
        let gridSizeX = 18
        let gridSizeY = 10
        let gridSizeZ = 10
        let spacing: Float = 0.05
        let startX: Float = -Float(gridSizeX - 1) * spacing * 0.5 + 0.3
        let startY: Float = -Float(gridSizeY - 1) * spacing * 0.5 - 0.1
        let startZ: Float = -Float(gridSizeZ - 1) * spacing * 0.5
        for i in 0..<gridSizeX {
            for j in 0..<gridSizeY {
                for k in 0..<gridSizeZ {
                    if j < 2 || j > gridSizeY - 5 { continue } // remove 2 bottom, 4 top
                    let cell = baseScene.clone(recursive: true)
                    cell.name = "Sphere"
                    cell.scale = .one * 0.02
                    cell.position = [
                        startX + Float(i) * spacing,
                        startY + Float(j) * spacing,
                        startZ + Float(k) * spacing
                    ]
                    cell.generateCollisionShapes(recursive: true)
                    gridCells.append(cell)
                    gridAnchor.addChild(cell)
                }
            }
        }
        content.add(gridAnchor)
        print("✅ Grid added")
    } catch {
        print("❌ Failed to load: \(error)")
    }
}

private func handleModelOrGridTap(_ tappedEntity: Entity) { guard let modelRootEntity = modelRootEntity else { return }

    let localPosition = tappedEntity.position(relativeTo: modelRootEntity)
    let worldPosition = tappedEntity.position(relativeTo: nil)

    switch tapStep {
    case 0:
        modelPointA = localPosition
        modelAnchor?.addChild(createMarker(at: worldPosition, color: [1, 0, 0]))
        print("📍 Model point A: \(localPosition)")
        tapStep += 1
    case 1:
        modelPointB = localPosition
        modelAnchor?.addChild(createMarker(at: worldPosition, color: [1, 0.5, 0]))
        print("📍 Model point B: \(localPosition)")
        tapStep += 1
        
    case 2:
        targetPointA = worldPosition
        targetMarkerA = createMarker(at: worldPosition,color: [0, 1, 0])
        modelAnchor?.addChild(targetMarkerA!)
        print("✅ Target point A: \(worldPosition)")
        tapStep += 1
        
    case 3:
        targetPointB = worldPosition
        targetMarkerB = createMarker(at: worldPosition,color: [0, 0, 1])
        modelAnchor?.addChild(targetMarkerB!)
        print("✅ Target point B: \(worldPosition)")
        alignmentReady = true
        tapStep += 1

    default:
        print("⚠️ Unexpected tap on model helper at step \(tapStep)")
    }
}

func alignModel2Points() { guard let modelPointA = modelPointA, let modelPointB = modelPointB, let targetPointA = targetPointA, let targetPointB = targetPointB, let modelRootEntity = modelRootEntity, let pivotEntity = pivotEntity, let modelAnchor = modelAnchor else { print("❌ Missing data for alignment") return }

    let modelVec = modelPointB - modelPointA
    let targetVec = targetPointB - targetPointA

    let modelLength = length(modelVec)
    let targetLength = length(targetVec)
    let scale = targetLength / modelLength

    let modelDir = normalize(modelVec)
    let targetDir = normalize(targetVec)

    var axis = cross(modelDir, targetDir)
    let axisLength = length(axis)

    var rotation = simd_quatf()

    if axisLength < 1e-6 {
        if dot(modelDir, targetDir) > 0 {
            rotation = simd_quatf(angle: 0, axis: [0,1,0])
        } else {
            let up: SIMD3<Float> = [0,1,0]
            axis = cross(modelDir, up)
            if length(axis) < 1e-6 {
                axis = cross(modelDir, [1,0,0])
            }
            rotation = simd_quatf(angle: .pi, axis: normalize(axis))
        }
    } else {
        let dotProduct = dot(modelDir, targetDir)
        let clampedDot = max(-1.0, min(dotProduct, 1.0))
        let angle = acos(clampedDot)
        rotation = simd_quatf(angle: angle, axis: normalize(axis))
    }

    modelRootEntity.scale = .one * scale
    modelRootEntity.orientation = rotation

    let transformedPointA = rotation.act(modelPointA * scale)

    pivotEntity.position = -transformedPointA

    modelAnchor.position = targetPointA
    
    alignedModelPosition = modelAnchor.position

    print("✅ Aligned with scale \(scale), rotation \(rotation)")

Hi @ArcSurgeryLab

I'm not sure why updating would cause a shift in entities. It sounds like you are trying to display a grid of entities, when someone taps an entity, in the grid, you want to position content relative to the tapped entity (not the tap location). Is that correct? If so here's a snippet that does that. Try comparing it to your code to it as a means of debugging.

What does stand out is your use of a world AnchorEntity. Keep in mind, querying the position of a world AnchorEntity or its children will return a position that does not account for the position you passed to the world AnchorEntity's constructor (in this case [1, 1.3, -0.8]). For example, in the snippet below, modelAnchor.position(relativeTo: nil) will return [0, 0, 0] and not [0, 1.3, -0.8].

struct ImmersiveView: View {
    let modelAnchor = AnchorEntity(world: [0, 1.3, -0.8])

    var body: some View {
        RealityView { content in
            let gridCell1 = createGridCell()
            let gridCell2 = createGridCell()
            gridCell2.position.x = 0.5
            
            modelAnchor.addChild(gridCell1)
            modelAnchor.addChild(gridCell2)
            content.add(modelAnchor)
        }
        .gesture(TapGesture().targetedToAnyEntity().onEnded { gesture in
            let tappedEntity = gesture.entity
            let position = tappedEntity.position
            let marker = createMarker(at: position, color: .red)
            
            modelAnchor.addChild(marker)
        })
    }
    
    func createGridCell() -> Entity {
        let cell = ModelEntity(mesh: .generateBox(size: 0.1), materials: [SimpleMaterial(color: .blue, isMetallic: false)])
        cell.generateCollisionShapes(recursive: false)
        cell.components.set(InputTargetComponent())
        
        return cell
    }
    
    func createMarker(at position: SIMD3<Float>, color: UIColor) -> Entity {
        let marker = ModelEntity(mesh: .generateSphere(radius: 0.02), materials: [SimpleMaterial(color: color, isMetallic: false)])
        
         marker.position = position
        
        // Offset the marker on the y-axis so you can see it.
        marker.position.y += 0.2
        return marker
    }
}
version update in Vision Pro
 
 
Q