Reputation: 637
I'm doing a non-AR 3D app on macOS using RealityKit and SwiftUI, and I need to be able to modify materials on objects based on the results of onContinuousHover. In particular, I want to highlight the face of a box that the mouse is hovering over, which requires being able to find the model position fairly accurately. This has been impossible so far.
To find out what I might be doing wrong, I prepared a plane:
var mesh: MeshResource = MeshResource.generatePlane(width: 10.0, depth: 10.0)
// material is a grid, to help visualize where hits are taking place
model = ModelComponent(mesh: mesh, materials: [material])
generateCollisionShapes(recursive: true)
collision = CollisionComponent(
shapes: [ShapeResource.generateConvex(from: model!.mesh)],
mode: .default
)
and placed it on an AnchorEntity positioned at world origin.
The view coordinates from the onContinuousHover closure are good, but the results from a call to hitTest (the one that returns [CollisionCastHit], not the deprecated [ARHitTestResult]) are really nonsensical:
The y coordinate of a hit on a X-Z Plane centered at world origin is consistently reported as 0.1 - any hit in this plane should be in the form (x, 0.0, z), the returned result is off by ten centimeters! Offsetting the plane changes the result by the offset, but the y value is still always off by a constant 0.1
The sign of the z coordinate is always reversed - hovering over a small sphere placed at world (0.0, 0.0, 0.1) always returns (0.0, 0.0, -0.1), placing it at (0.0, 0.0, -0.1) and hovering returns (0.0, 0.0, 0.1). This holds for any point in the plane.
The Entity in the CollisionCastHit is always correct, but the reported model position is wildly inaccurate. The transform for both the plane and its anchor is the identity transform, no scale, translation or rotation. And I am always careful to convert to/from world space correctly, but in this test case, they are identical.
I was going to try using arview.raycast(from: allowing: alignment:)
, which the documentation says is available on macOS as of 10.15+, but I get "Value of type 'ARView' has no member 'raycast'". Opening the RealityKit header and searching for "raycast" got zero hits - where is it?
Ideas?
Upvotes: 0
Views: 422
Reputation: 637
After reading the answer from maxxfraser, I began to suspect that the problem had its roots in a mismatch between SwiftUI and NSViewRepresentable. I spent a TSI and got this from Apple DTS:
The cause of the incorrect results you are seeing is because there is a mismatch between SwiftUI’s view coordinate system and NSView’s default coordinate system.
SwiftUI says that the origin of a view is the top-left corner. NSView defaults its origin to the bottom left corner.
A simple way for you to resolve this in the app you provided is to override isFlipped in your “RepresentedARView”:
public override var isFlipped: Bool { true }
With that simple change, I believe I am seeing correct results in your project.
And true it was! I wasn't aware of isFlipped, and missed it when I was looking for a solution. I can see this causing all sorts of mischief in any NSViewRepresentable. BTW, RepresentedARView is a subclass of ARView.
Thanks to Chris at Apple DTS for the quick, correct (and simple!) answer.
Upvotes: 0
Reputation: 1253
It looks like the documentation is making a mistake by saying it's available on macOS. If you go from the method here, to one of the arguments ARRaycastQuery.Target, you can see ARRaycastQuery is not available on macOS; so a method that uses it also cannot be available for macOS!
I'd recommend instead using hitTest(_:query:mask:), which should give you everything you need and works well whenever I use it.
It's used extensively in RealityUI, so can vouch for its accuracy and reliability:
Try turning on physics with debugOptions and showPhysics. The Y coordinate being off by 10cm sounds a lot like the box that has been created has a height of 0.2, instead of being a flat plane.
Upvotes: 0