I apologize for forgetting important information.
Machine: MacBook Pro 16" M1Max w/ 64 Gigs of memory (brand spanking new and loving the GPU for raytracing!)
OS: Monterey 12.0.1
There are no RealityKit physics simulations on. I have confirmed this by checking that the PhysicsBodyComponent and PhysicsMotionComponent are both nil. I only have ModelComponent, TransformComponent, and the synchronization component that is always present I believe.
I am including the important code at the end of this post. I should note that I put this entity into an ARView that has an environment.lighting.resource
and an environment.background
as you can probably see from my original attached image. I am currently testing whether getting rid of these helps performance but I am not expecting a big difference. I generated a RKView subclass of ARView that is basically recreating SceneKits display mode that allowed for mouse camera control, and 3D display without augmented reality since my applications are not really augmented-reality based and more virtual-reality based.
Finally, I summarize the code by noting that I create a single Box Entity with one material. I then clone this entity in an 8x6 grid. Finally, I clone this 8x6 grid 64x4 times to create the ring of detectors to model my system. The entity
variable is what is displayed.
I appreciate all the support on these forums. It's a great help to get direct feedback from people well connected with the Apple developers!
-Matt
public class PETRingSystem: ObjectProviding {
let ringDiameter:Float = 0.81 // 81 cm
let crystalSize = SIMD3<Float>(0.0047, 0.0063, 0.03) // (4.7mm x 6.3mm x 3cm)
// Spacing between crysals. 0.2 mm
let spacing: Float = 0.000274
// Crystal blocks are 8x6
let blockDim = SIMD2<Int>(8, 6) // in number of detectors
// Compute the block size from the crystal size, blockDim, and spacing
var blockSize: SIMD2<Float> {
get {
let ang = Float(blockDim.x) * (crystalSize.x + spacing)
let z = (Float(blockDim.y) * crystalSize.y) + (Float(blockDim.y - 1) * spacing)
return(SIMD2<Float>(ang,z))
}
}
// How many blocks in the ring
let blocksInRing = SIMD2<Int>(64, 4)
// The axian field of view
let axialFOV:Float = 0.1576
// blanks for the crystal Entity that is cloned and the block entity that is also cloned
var crystal = Entity()
var block = Entity()
// The final entity that is displayed
public var entity = Entity()
/// Duplicate the blocks around the origin (0,0,0) to create the ring of detectors around the patient's head
public func populateRing() {
let s = blockSize.y
let f = axialFOV
for zIdx in 0..<(blocksInRing.y) {
for angIdx in 0..<(blocksInRing.x) {
let node = block.clone(recursive: true)
entity.addChild(node)
var offset:Float = 0.0
if (blocksInRing.y == 1) {
offset = 0.0
} else {
offset = (Float(zIdx) * (f - s) / (Float(blocksInRing.y - 1)))
offset -= (f - s) / 2.0
}
let ang = Float(angIdx) * 2.0 * Float.pi / Float(blocksInRing.x)
// Move the block to -ringDiameters/2 in the y direction, then rotate to the proper angle
node.transform.matrix = Transform(translation: SIMD3<Float>(0, -ringDiameter/2.0, offset)).matrix * node.transform.matrix
node.transform.matrix = Transform(pitch: 0.0, yaw: 0.0, roll: ang).matrix * node.transform.matrix
}
}
}
/// Create the ring of PET detectors
public init() {
// Create the mesh for the cubic detector
let box = MeshResource.generateBox(size: crystalSize)
// The display material
let material = SimpleMaterial(color: .blue, roughness: 0.1, isMetallic: false)
// Create the base entity
let crystalRaw = ModelEntity(mesh: box, materials: [material])
// Shift the raw crystal so that the origin is centered on the entrance face
crystalRaw.position = SIMD3<Float>(0.0, 0.0, crystalSize.z / 2.0)
// The actual Entity used to create the ring of detectors
crystal = Entity()
crystal.addChild(crystalRaw)
// The ring of detector is organized in 8x6 blocks. This creates one block by
// cloning the crystal
let blockRaw = Entity()
for i in 0..<blockDim.x { // 8
for j in 0..<blockDim.y { // 6
let crystalInBlock = crystal.clone(recursive: true)
blockRaw.addChild(crystalInBlock)
crystalInBlock.position = SIMD3<Float>(Float((crystalSize.x + spacing) * Float(i)), Float((crystalSize.y + spacing) * Float(j)), Float(0.0))
}
}
block.addChild(blockRaw)
// Center the origin of the block directly in the middle of all the crystal detectors
blockRaw.position = SIMD3<Float>(-Float(blockDim.x - 1) * (crystalSize.x + spacing) / 2.0,
-Float(blockDim.y - 1) * (crystalSize.y + spacing) / 2.0,
0.0)
// Transform so that the detectors are facign up
blockRaw.transform.matrix = Transform(pitch: Float.pi/2, yaw: 0.0, roll: 0.0).matrix * blockRaw.transform.matrix
// This function clones the blocks and places them in the ring around (0,0,0)
populateRing()
}
}