How to use ClothGrabComponent?

I've simulated a piece of cloth using https://developer.apple.com/documentation/realitykit/physics-cloth-simulation

I would like to grab the cloth like what I can in Marvellous Designer.

I found the ClothGrabComponent at https://developer.apple.com/documentation/realitykit/clothgrabcomponent

However, after I added the ClothGrabComponent to the cloth entity, I am still not able to grab it.

Is there any sample code to show me how to grab the cloth? Thank you.

Answered by Vision Pro Engineer in 905757022

Hi @jamesic,

ClothGrabComponent isn't a component you attach directly to your cloth entity to make it grab-able; instead, it's a component you attach to a separate "grabber" entity which you can then use to grab and manipulate the cloth.

For example, you can create a cloth grabber entity with a ClothGrabComponent like so:

let clothGrabber = Entity(components: [ModelComponent(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial()]),
                                       OpacityComponent(opacity: 0.5),
                                       InputTargetComponent(),
                                       CollisionComponent(shapes: [.generateSphere(radius: 0.1)]),
                                       ClothGrabComponent(mode: .volume(shape: .sphere(.init(radius: 0.1))))])

Here, I've set the cloth grab component's mode to volume(shape:) so that it grabs cloth within a 0.1m radius sphere of its position, but you could use the ray mode to grab cloth the grabber entity is pointing toward instead. I've also attached a ModelComponent to make the grabber visible in the scene, and an InputTargetComponent and a CollisionComponent to make it targetable by gestures.

Next, you can write a gesture that makes this entity respond to drag inputs:

@State var isDragging = false
@State var dragStartPosition: SIMD3<Float> = .zero

// ...

.gesture(
    // Make the cloth grabber draggable.
    DragGesture(minimumDistance: 0)
        .targetedToEntity(clothGrabber)
        .onChanged { event in
            if !isDragging {
                isDragging = true
                dragStartPosition = clothGrabber.position
                // Start grabbing the cloth when the drag gesture begins.
                clothGrabber.components[ClothGrabComponent.self]?.isGrabbing = true
            }
            // Move the grabber with the drag translation.
            let translation = event.convert(event.translation3D, from: .local, to: .scene)
            clothGrabber.position = dragStartPosition + translation
        }.onEnded { event in
            isDragging = false
            // Stop grabbing the cloth when the drag gesture ends.
            clothGrabber.components[ClothGrabComponent.self]?.isGrabbing = false
        }
)

By setting the cloth grab component's isGrabbing property to true when the drag gesture begins, and to false when the drag gesture ends, you can now grab the cloth with the grabber by initiating a drag when it overlaps the cloth, and release the cloth by ending the drag.

To achieve more natural interactions, consider attaching the InputTargetComponent and CollisionComponent to the cloth entity itself (so that it can be the target of the drag gesture instead of the grabber) and positioning the cloth grabber entity at the hit location of the drag gesture on the cloth. One caveat to this approach is that you'll need to use a collision shape that closely conforms to the shape of your cloth, which can be difficult given that the cloth itself is deformable. Depending on your use case, you may find that making a static surface your cloth is in close-contact with the gesture target yields even better results. For example, if your cloth is simulating clothing affixed to a static character model, consider targeting your gestures to the character model itself and using those interaction locations to drive the grab component.

Let me know if you have any questions!

Accepted Answer

Hi @jamesic,

ClothGrabComponent isn't a component you attach directly to your cloth entity to make it grab-able; instead, it's a component you attach to a separate "grabber" entity which you can then use to grab and manipulate the cloth.

For example, you can create a cloth grabber entity with a ClothGrabComponent like so:

let clothGrabber = Entity(components: [ModelComponent(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial()]),
                                       OpacityComponent(opacity: 0.5),
                                       InputTargetComponent(),
                                       CollisionComponent(shapes: [.generateSphere(radius: 0.1)]),
                                       ClothGrabComponent(mode: .volume(shape: .sphere(.init(radius: 0.1))))])

Here, I've set the cloth grab component's mode to volume(shape:) so that it grabs cloth within a 0.1m radius sphere of its position, but you could use the ray mode to grab cloth the grabber entity is pointing toward instead. I've also attached a ModelComponent to make the grabber visible in the scene, and an InputTargetComponent and a CollisionComponent to make it targetable by gestures.

Next, you can write a gesture that makes this entity respond to drag inputs:

@State var isDragging = false
@State var dragStartPosition: SIMD3<Float> = .zero

// ...

.gesture(
    // Make the cloth grabber draggable.
    DragGesture(minimumDistance: 0)
        .targetedToEntity(clothGrabber)
        .onChanged { event in
            if !isDragging {
                isDragging = true
                dragStartPosition = clothGrabber.position
                // Start grabbing the cloth when the drag gesture begins.
                clothGrabber.components[ClothGrabComponent.self]?.isGrabbing = true
            }
            // Move the grabber with the drag translation.
            let translation = event.convert(event.translation3D, from: .local, to: .scene)
            clothGrabber.position = dragStartPosition + translation
        }.onEnded { event in
            isDragging = false
            // Stop grabbing the cloth when the drag gesture ends.
            clothGrabber.components[ClothGrabComponent.self]?.isGrabbing = false
        }
)

By setting the cloth grab component's isGrabbing property to true when the drag gesture begins, and to false when the drag gesture ends, you can now grab the cloth with the grabber by initiating a drag when it overlaps the cloth, and release the cloth by ending the drag.

To achieve more natural interactions, consider attaching the InputTargetComponent and CollisionComponent to the cloth entity itself (so that it can be the target of the drag gesture instead of the grabber) and positioning the cloth grabber entity at the hit location of the drag gesture on the cloth. One caveat to this approach is that you'll need to use a collision shape that closely conforms to the shape of your cloth, which can be difficult given that the cloth itself is deformable. Depending on your use case, you may find that making a static surface your cloth is in close-contact with the gesture target yields even better results. For example, if your cloth is simulating clothing affixed to a static character model, consider targeting your gestures to the character model itself and using those interaction locations to drive the grab component.

Let me know if you have any questions!

How to use ClothGrabComponent?
 
 
Q