swiftuirealitykitvisionos

Scale volumetric window content with user resize


I'm trying to scale a volumetric window's content when the user resizes the window. When I use modifier aspectRatio(contentMode: .fill), the content does not resize. Using scaledToFit() doesn't scale, and it inexplicably distorts both the window and the content.

How do I scale the content of a volumetric window when the window is resized?

import RealityKit
import SwiftUI

@main
struct SandboxApp: App {
    var body: some SwiftUI.Scene {
        WindowGroup {
            ContentView()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 1, height: 1, depth: 1, in: .meters)
    }
}

struct ContentView: View {
    var body: some View {
        RealityView { content in
            content.add(ModelEntity(mesh: MeshResource.generateSphere(radius: 0.25),
                                    materials: [SimpleMaterial(color: .red, isMetallic: false)]))
        }
//        .aspectRatio(contentMode: .fill) // does not resize
//        .scaledToFill() // does not resize AND distorts volume and content
    }
}

Solution

  • You can do it if you hang on to the Entity and use setScale based on the GeometryProxy3D from GeometryReader3D.

    import SwiftUI
    import RealityKit
    struct BasicPhotoView<E>: View where E : Entity & HasModel  {
        var model: E
        var body: some View {
            GeometryReader3D { proxy in
                RealityView { content in
                    
                    content.add(model)
                } update: { content in
                    let size = content.convert(proxy.frame(in: .local), from: .local, to: .scene)
                    model.resize(box: size)
                }
            }
        }
    }
    
    extension Entity : HasModel {
        public func resize(box: BoundingBox, style: Style = .aspectRatio) {
            print(#function)
            
            guard let meshBounds = self.model?.mesh.bounds.extents else {
                print("no model")
                return
            }
            
            // Convert extents to positive size vectors
            let spaceSize = simd_float3(Float(box.extents.x), Float(box.extents.y), Float(box.extents.z))
            let meshSize = simd_float3(meshBounds.x, meshBounds.y, meshBounds.z)
            
            // Calculate scale factors for each dimension
            let scaleFactor = spaceSize / meshSize
            
            switch style {
            case .fill:
                // Use the exact scale factor for each dimension (non-uniform scaling)
                self.setScale(abs(scaleFactor), relativeTo: nil)
                
            case .aspectRatio:
                // Use the minimum scale factor for uniform scaling
                let uniformScale = min(scaleFactor.x, scaleFactor.y, scaleFactor.z)
                let aspectRatioScale = simd_float3(repeating: uniformScale)
                self.setScale(abs(aspectRatioScale), relativeTo: nil)
            }
        }
    }