Spatial SwiftUI: Physical Metrics

Scaling entities to exact real world metrics.

Overview

SwiftUI has some features to calculate the number of points needed to represent real-world sizes. We can convert these points to values we can use in RealityKit to scale or size entities.

There are two related features here

  1. The Physical Metric property wrapper
  2. The Physical Metrics Converter, which we can get from the environment

We’ll create a box that is scaled to one foot on all sides. The variable oneFoot will be set with number of points needed to represent a single foot in length.

@PhysicalMetric(from: .feet) var oneFoot: Float = 1

print("one foot in points is: \(oneFoot)")

This will print 414.528. As you can guess, that is not a suitable number to scale an entity in RealtyKit.

Let’s get the converter

@Environment(\.physicalMetrics) var physicalMetrics

We can use the converter to take the points in oneFoot and convert them to meters that we can use in the RealityKit scene.

Personal preference: Even if I need to present 3D content scaled as feet, I’d prefer to keep my scene content in meters.

let sizeInPhysicalMetric = physicalMetrics.convert(oneFoot, to: .meters)
subject.scale = .init(repeating: sizeInPhysicalMetric)

print("box has scale: \(sizeInPhysicalMetric) in meters")
  • We asked SwiftUI for points that represent one foot = 414.528
  • We converted those points to meters = 0.3048
  • Then we scaled our entity

It is important to point out that this scaling works because our box was created a 1 meter on all sides. It may be interesting to look at methods to use physical metrics to set the size of entities.

Physical metrics demo

Example Code

struct Example080: View {

    // Get the converter from the environment
    @Environment(\.physicalMetrics) var physicalMetrics

    // Set a variable that will calculate the points needed to represent one foot
    @PhysicalMetric(from: .feet) var oneFoot: Float = 1

    // Set up a subject entity
    @State var subject: Entity = {
        let mat = SimpleMaterial(color: .stepGreen, roughness: 0.2, isMetallic: false)
        let box = ModelEntity(
            mesh: .generateBox(width: 1, height: 1, depth: 1),
            materials: [mat])
        return box
    }()

    var body: some View {

            RealityView { content in
                content.add(subject)

                // We can print the number of points to represent one foot
                print("one foot in points is: \(oneFoot)")

                // We'll convert from points to a size in meters and scale the subject
                let sizeInPhysicalMetric = physicalMetrics.convert(oneFoot, to: .meters)
                subject.scale = .init(repeating: sizeInPhysicalMetric)

                print("box has scale: \(sizeInPhysicalMetric) in meters")

            }

    }
}

Support our work so we can continue to bring you new examples and articles.

Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?