Search code examples
swiftaugmented-realityrealitykit

RealityKit – Scale a model from its center


I would like to scale a 3D model (a tall toy robot) in place, i.e. from its center: it should grows and shrink on all dimensions, without changing position.

I’m able to scale the toy robot model, but the model scales up or down from its feet, as opposed to its proper center.

I’ve tried scaling via model.scale = ....

I’ve also tried using the model.move method, as recommended in this answer.

My code:

let newTransform = Transform(scale: .init(x: myScale.x, 
                                          y: myScale.y, 
                                          z: myScale.z))

modelEntity.move(to: newTransform, relativeTo: modelEntity, duration: 1.0)

arAnchor.addChild(modelEntity)

Solution

  • Scaling model from its center

    Rotation and scaling of models is performed relative to their pivot point. If the pivot is located on the lower border of the Bounding Box (for a robot model, this is the correct location of the pivot), then if scaling it up, the model will "grow up" from the "floor". If you want to scale the model from its center, then create a new parent Entity (since RealityKit still doesn't have simdPivot property, like in SceneKit), translate it to model's center (however, do not forget to compensate robot's position), and use its pivot point as the scale's origin.

    Here's a code:

    import SwiftUI
    import RealityKit
    
    struct ContentView : View {
        var body: some View {
            ARViewContainer().ignoresSafeArea()
        }
    }
    

    struct ARViewContainer: UIViewRepresentable {
        
        let arView = ARView(frame: .zero)
        
        func makeUIView(context: Context) -> ARView {
            
            let robot = try! ModelEntity.load(named: "toy_robot.usdz")
            
            let scalingPivot = Entity()
            scalingPivot.position.y = robot.visualBounds(relativeTo: nil).center.y
            scalingPivot.addChild(robot)
            
            // compensating a robot position
            robot.position.y -= scalingPivot.position.y
    
            let anchor = AnchorEntity()
            anchor.addChild(scalingPivot)
            arView.scene.addAnchor(anchor)
            
            let newTransform = Transform(scale: .one * 7)
            scalingPivot.move(to: newTransform, 
                      relativeTo: scalingPivot.parent, 
                        duration: 5.0)
            return arView
        }
        func updateUIView(_ view: ARView, context: Context) { }
    }
    

    There's also a visionOS solution.

    enter image description here