I tested all variations. The checkboxes in Reality Composer Pro 2 (beta 4) in the Physics Body component:
are absolute and not parent-relative. Also, regardless of what I set the center of mass to:
it always rotates around the center of mass despite the local rotation being correctly at the center of origin (imported from Blender). Thus I can get the door to turn but never to swing because it always rotates around its center of mass.
Tell me if this is expected behaviour or if there is a simple way to make this work.
Hi @a.p.
The key to getting dynamic physics bodies to work in this approach is to use addForce instead of manually setting their positions. This ensures the physics bodies interact with other objects in the physics simulation in a natural way.
I spent some time modifying your code to get this to work and here's what I came up with:
First, create a component and a system responsible for adding force to the hand joints to move them toward a target position. This system will be used to move the hand joints to their tracked real world positions using forces instead of directly setting their positions.
/// Component that stores the current target position of a hand joint.
struct DynamicTargetComponent: Component {
var targetPosition: SIMD3<Float>
}
/// System that adds force to the hand joints' dynamic physics bodies to move them towards their target positions.
struct DynamicTargetSystem: System {
let query = EntityQuery(where: .has(DynamicTargetComponent.self))
public init(scene: RealityKit.Scene) { }
public func update(context: SceneUpdateContext) {
let handJointEntities = context.entities(matching: self.query, updatingSystemWhen: .rendering)
for handJointEntity in handJointEntities {
guard let handJointModelEntity = handJointEntity as? ModelEntity,
let physicsBody = handJointModelEntity.components[PhysicsBodyComponent.self],
let dynamicHandJointComponent = handJointModelEntity.components[DynamicTargetComponent.self] else { return }
// Get the vector pointing in the direction of the target position.
let toTarget = dynamicHandJointComponent.targetPosition - handJointModelEntity.position(relativeTo: nil)
// Add force to the hand joint in that direction, multiplying by mass so that this
// works consistently no matter what the mass of the hand joint itself is.
let forceFactor: Float = 5000 * physicsBody.massProperties.mass
handJointModelEntity.addForce(toTarget * forceFactor, relativeTo: nil)
}
}
}
Then, be sure to register the DynamicTargetSystem
. I do this at the top of the RealityView
.
RealityView { content in
DynamicTargetSystem.registerSystem()
//...
}
Finally, replace the for
loop iterating over every joint
in the handSkeleton
in your code with the following snippet.
for joint in handSkeleton.allJoints.filter({ $0.isTracked }) {
let partID = "\(joint.name)-\(anchor.chirality)"
var part = parts[partID]
// Get the transform matrix for the current joint.
let transform = anchor.originFromAnchorTransform * joint.anchorFromJointTransform
// Extract its position from the transform matrix.
let jointPosition = simd_make_float3(transform.columns.3)
if part == nil {
part = ModelEntity(mesh: .generateSphere(radius: 0.01), materials: [mat3])
part!.generateCollisionShapes(recursive: false)
part!.components.set(OpacityComponent(opacity: 0.5))
// Add a dynamic physics body component not affected by gravity.
var physicsBody = PhysicsBodyComponent(mode: .dynamic)
physicsBody.isAffectedByGravity = false
physicsBody.massProperties.mass = 10 // Increase mass to prevent the door from moving the hand too much.
physicsBody.linearDamping = 50 // Use absurdly high linear damping to reduce overshoot and springiness.
part!.components.set(physicsBody)
// Prevent the hand joints from being able to collide with one another using a collision filter.
let handJointGroup = CollisionGroup(rawValue: 1 << 1)
let allButHandJointGroup = CollisionGroup.all.subtracting(handJointGroup)
let allButHandJointFilter = CollisionFilter(group: handJointGroup, mask: allButHandJointGroup)
part!.components[CollisionComponent.self]?.filter = allButHandJointFilter
// Add a `DynamicTargetComponent` to the joint.
part!.components.set(DynamicTargetComponent(targetPosition: jointPosition))
// Set the starting position of the joint.
part!.position = jointPosition
handRoot.addChild(part!)
parts[partID] = part
}
// Set the joint's target position to be its current tracked position.
// The `DynamicTargetSystem` will then try to move the joint toward this position every frame using forces.
part?.components[DynamicTargetComponent.self]?.targetPosition = jointPosition
}
You should now be able to use your hands to interact with the door. You can even pinch the door to rotate it around!
This approach isn't perfect. Colliders can get caught on the door and temporarily lag behind the hands as I mentioned in my previous response. That being said, there are a number of variables you can tweak to adjust this approach to your liking:
mass
: Increase this value to increase the rigidity of the hands and improve their ability to boss around the door and other physics objects in the scene. Decrease this value to allow the door and other physics objects to move the hands and behave more naturally when colliding.linearDamping
: Increase this value to increase the rate at which the hand joints come to rest. This is useful for preventing them from overshooting their targets, but can cause them to move too slowly if increased too high.forceFactor
: Increase this value to increase the speed at which the hand joints reach their target positions—but be careful—if you increase this value too high the hand joints will overshoot and oscillate around their target positions.