Rotate an entity in VisionPro

Hi,

I'm trying to rotate an entity in VisionPro. Most of the code is the same as the Diorama code from WWDC23.

The problem I'm having is that the rotiation occurs but the axis of the rotation is not the center of my object.

It seems to be centered on the zero coordinate of the immersive space . How do I change the rotation3DEffect to tell it to rotate around the entity? Not the space? Is it even possible?

This is the code, the rotation is at the end.

var body: some View { @Bindable var viewModel = viewModel

    RealityView { content, _ in
        do {
            let entity = try await Entity(named: "DioramaAssembled", in: RealityKitContent.RealityKitContentBundle)
            viewModel.rootEntity = entity
            content.add(entity)
            viewModel.updateScale()
            
            // Offset the scene so it doesn't appear underneath the user or conflict with the main window.
            entity.position = SIMD3<Float>(0, 0, -2)
            

            subscriptions.append(content.subscribe(to: ComponentEvents.DidAdd.self, componentType: PointOfInterestComponent.self, { event in
                createLearnMoreView(for: event.entity)
            }))

            entity.generateCollisionShapes (recursive: true)
            entity.components.set(InputTargetComponent())
   
        } catch {
            print("Error in RealityView's make: \(error)")
        }
        
    } 

   .rotation3DEffect(.radians(currentrotateByX), axis: .y)
   .rotation3DEffect(.radians(currentrotateByY), axis: .x)

Replies

I am looking for the exact solution. My Entity rotates around myself (the standing position of the person wearing Vision Pro), and not on its own axis. I want Entities to rotate on their own axes. Let me know if you figure this out.

Just for the context, I can rotate Entities on their own only if it's a WindowGroup with a volumetric type. So if I define my Space like this:

WindowGroup(id: "hello") {
            Hello()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 1, height: 1, depth: 1, in: .meters)

it works, and my Entity rotates on its own axis with this code .rotation3DEffect(.radians(currentrotateByX), axis: .y)

But if I declare the space like this:

ImmersiveSpace(id: "hello") {
            Hello()
        }

it starts rotating around me.

A smart person once showed me how:

    @State var lastGestureValue = CGFloat(0)
    
    var body: some View {
        
        RealityView { content in
            let diskMaterial = SimpleMaterial(color: .lightGray, roughness: 0.5, isMetallic: false)
            let disk = ModelEntity(mesh: .generateCylinder(height: 0.03, radius: 0.47), materials: [diskMaterial])
            disk.name = "platter"
            
            disk.generateCollisionShapes(recursive: false)
            disk.components.set(InputTargetComponent())
            disk.components.set(HoverEffectComponent())
            
            disk.position = [0, 1, -1.5]
            disk.components.set([GroundingShadowComponent(castsShadow: true)])
            disk.addChild(ModelEntity(mesh: .generateSphere(radius: 0.3)))
            content.add(disk)
            
        }
        .gesture(
            DragGesture()
                .targetedToAnyEntity()
                .onChanged { value in
                    let entity = value.entity
                    let orientation = Rotation3D(entity.orientation(relativeTo: nil))
                    let newOrientation: Rotation3D

                    if (value.location.x >= lastGestureValue) {
                        newOrientation = orientation.rotated(by: .init(angle: .degrees(0.5), axis: .y))
                    } else {
                        newOrientation = orientation.rotated(by: .init(angle: .degrees(-0.5), axis: .y))
                    }
                    entity.setOrientation(.init(newOrientation), relativeTo: nil)
                    lastGestureValue = value.location.x
                }
        )
    }

Thanks a lot!!!!!

Is it possible to rotate in both axes? I tried to duplicate the movement for both axes (changing .x and .y where appropriate) but it only seems to rotate in the second axis I'm sure I made a mistake but I cannot find it:

   .gesture(
                DragGesture()
                    .targetedToAnyEntity()
                    .onChanged { value in
                        let entity = value.entity
                        let orientation = Rotation3D(entity.orientation(relativeTo: nil))
                        let newOrientationX: Rotation3D
                        let newOrientationY: Rotation3D

                        if (value.location.x >= lastGestureValueX) {
                            newOrientationX = orientation.rotated(by: .init(angle: .degrees(2.0), axis: .y))
                        } else {
                            newOrientationX = orientation.rotated(by: .init(angle: .degrees(-2.0), axis: .y))
                        }
                        
                        entity.setOrientation(.init(newOrientationX), relativeTo: nil)
                        lastGestureValueX = value.location.x
                        
                        
                        if (value.location.y >= lastGestureValueY) {
                            newOrientationY = orientation.rotated(by: .init(angle: .degrees(2.0), axis: .x))
                        } else {
                            newOrientationY = orientation.rotated(by: .init(angle: .degrees(-2.0), axis: .x))
                        }
                        
                        entity.setOrientation(.init(newOrientationY), relativeTo: nil)
                        lastGestureValueY = value.location.y
                        
                        
                        
                    }
            )



I found the problem. The orientation needed to be a var and reset to the current orientation between moving the second axis.

                DragGesture()
                    .targetedToAnyEntity()
                    .onChanged { value in
                        let entity = value.entity
                        var orientation = Rotation3D(entity.orientation(relativeTo: nil))
                        var newOrientation: Rotation3D
//                      let newOrientationY: Rotation3D

                        if (value.location.x >= lastGestureValueX) {
                            newOrientation = orientation.rotated(by: .init(angle: .degrees(1.0), axis: .y))
                        } else {
                            newOrientation = orientation.rotated(by: .init(angle: .degrees(-1.0), axis: .y))
                        }
                        entity.setOrientation(.init(newOrientation), relativeTo: nil)
                        lastGestureValueX = value.location.x
                        
                        orientation = Rotation3D(entity.orientation(relativeTo: nil))
                        if (value.location.y >= lastGestureValueY) {
                            newOrientation = orientation.rotated(by: .init(angle: .degrees(1.0), axis: .x))
                        } else {
                            newOrientation = orientation.rotated(by: .init(angle: .degrees(-1.0), axis: .x))
                        }
                        entity.setOrientation(.init(newOrientation), relativeTo: nil)
                        lastGestureValueY = value.location.y
                        
                        
                        
                    }
            )

@Technology Evangelist If you know how to rotate not just the entity but also the attachments connected to it, please let me know.

Thanks Michele

Any entity you've attached to the target entity via addChild should rotate with it. Is that not happening?

Hi,

Yes, I did. I checked with the debugger, and it goes through that line for each attachment. Note that if I use my "old" gesture code, it rotates along some other axis (I'm not sure which one the attachment moves> Here is the other code, which rotates the whole thing including the attachments

Baffling

.gesture( DragGesture(minimumDistance: 0.0) .targetedToAnyEntity() .onChanged {value in let location3d = value.convert(value.location3D, from: .local, to: .scene) let startLocation = value.convert(value.startLocation3D, from: .local, to: .scene) let delta = location3d - startLocation rotateByX = Double(atan(delta.x * 0.1)) rotateByY = Double(atan(delta.y * 0.1)) currentrotateByX = currentrotateByX + rotateByX currentrotateByY = currentrotateByY + rotateByY } )