Hi @YuThomas
RealityKit provides a raycast
method which should allow you to achieve a similar setup. For example, you can translate that bit of code into Swift as follows:
| func calculateLengthInsideOrgan(scene: RealityKit.Scene, |
| probeBasePosition: SIMD3<Float>, |
| probeTipPosition: SIMD3<Float>, |
| organCollisionGroup: CollisionGroup) -> Float { |
| |
| let hits = scene.raycast(from: probeBasePosition, to: probeTipPosition, mask: organCollisionGroup) |
| |
| |
| |
| if let firstHit = hits.first { |
| let distanceToFirstHit = firstHit.distance |
| return distance(probeTipPosition, probeBasePosition) - distanceToFirstHit |
| } else { |
| return 0 |
| } |
| } |
Additionally, here's some sample code that demonstrates one way you could integrate this function into a RealityKit project with the behavior you're describing:
| @State var probeBaseEntity: Entity = Entity() |
| @State var lengthInsideOrgan: Float = 0 |
| |
| var body: some View { |
| RealityView { content, attachments in |
| |
| let organEntity = Entity() |
| organEntity.components.set(ModelComponent(mesh: .generateSphere(radius: 0.25), materials: [SimpleMaterial()])) |
| |
| |
| let organCollisionGroup = CollisionGroup(rawValue: 1 << 1) |
| let organCollisionFilter = CollisionFilter(group: organCollisionGroup, mask: .all) |
| |
| let collisionComponent = CollisionComponent(shapes: [.generateSphere(radius: 0.25)], filter: organCollisionFilter) |
| organEntity.components.set(collisionComponent) |
| |
| |
| let probeLength = Float(0.25) |
| let probeMaterial = SimpleMaterial(color: .gray, isMetallic: true) |
| probeBaseEntity = ModelEntity(mesh: .generateSphere(radius: 0.02), materials: [probeMaterial]) |
| |
| probeBaseEntity.generateCollisionShapes(recursive: false) |
| probeBaseEntity.components.set(InputTargetComponent()) |
| |
| let probeBodyEntity = Entity() |
| probeBodyEntity.components.set(ModelComponent(mesh: .generateBox(width: probeLength, height: 0.01, depth: 0.01), materials: [probeMaterial])) |
| probeBaseEntity.addChild(probeBodyEntity) |
| probeBodyEntity.position = [probeLength / 2, 0, 0] |
| |
| let probeTipEntity = Entity() |
| probeBaseEntity.addChild(probeTipEntity) |
| probeTipEntity.position = [probeLength, 0, 0] |
| |
| |
| organEntity.position = [0, 1, -1] |
| content.add(organEntity) |
| probeBaseEntity.position = [-0.6, 1, -1] |
| content.add(probeBaseEntity) |
| |
| |
| if let textAttachmentEntity = attachments.entity(for: "TextAttachment") { |
| probeBaseEntity.addChild(textAttachmentEntity) |
| textAttachmentEntity.position = [-0.1, 0, 0] |
| } |
| |
| |
| _ = content.subscribe(to: SceneEvents.Update.self) { event in |
| |
| let probeBaseWorldPosition = probeBaseEntity.position(relativeTo: nil) |
| let probeTipWorldPosition = probeTipEntity.position(relativeTo: nil) |
| |
| |
| lengthInsideOrgan = calculateLengthInsideOrgan(scene: event.scene, |
| probeBasePosition: probeBaseWorldPosition, |
| probeTipPosition: probeTipWorldPosition, |
| organCollisionGroup: organCollisionGroup) |
| } |
| } attachments: { |
| |
| Attachment(id: "TextAttachment") { |
| Text("Length: \(lengthInsideOrgan)") |
| .padding() |
| .glassBackgroundEffect() |
| } |
| }.gesture( |
| // Allow the base of the probe to be moved with a drag gesture. |
| DragGesture() |
| .targetedToEntity(probeBaseEntity) |
| .onChanged({ value in |
| probeBaseEntity.position = value.convert(value.location3D, |
| from: .local, |
| to: .scene) |
| }) |
| ) |
| } |
This code creates a sphere model with a CollisionComponent
to act as an "organ," along with a needle-like entity to act as a "probe." The probe can be dragged around, and it displays the output of the calculateLengthInsideOrgan()
method with an attachment that floats in space next to the probe.
Hope this helps, and let me know if you have any question!