The skeletal model moves

I want to make a model with added bones move by dragging it with gestures,This is a model exported using Blender, What I understand is using IKComponent, but I don't know how to use it specifically

Hi @LazyCoder

Here's a snippet to get you started. The snippet enables you to manipulate a humanoid robot by dragging sphere's attached to its hands ands feet. The snippet uses the model available for download here. For more information on the model format, read Rigging a Model for Motion Capture.

To run the snippet, download the model and include it in your project. The snippet looks for a model named "biped_robot".

Please reach out if you have followup questions. If this answers your question please accept the answer.

Here's the source for ImmersiveView. I'll share the code for the SampleModel in my next response.

struct ImmersiveView: View {
@State fileprivate var model = SampleModel()
@State var subscription: EventSubscription?
@State var humanEntity = Entity()
@State var leftHandTarget:ModelEntity
@State var rightHandTarget:ModelEntity
@State var leftFootTarget:ModelEntity
@State var rightFootTarget:ModelEntity
let root = Entity()
init() {
let alpha = 0.65
leftHandTarget = Self.createTargetEntity(color: .blue.withAlphaComponent(alpha))
rightHandTarget = Self.createTargetEntity(color: .red.withAlphaComponent(alpha))
leftFootTarget = Self.createTargetEntity(color: .orange.withAlphaComponent(alpha))
rightFootTarget = Self.createTargetEntity(color: .green.withAlphaComponent(alpha))
}
var body: some View {
RealityView { content in
guard let character = try? await ModelEntity(named: "biped_robot") else {
fatalError("Failed to load character model.")
}
character.transform.translation = [0, 0, -1]
character.transform.scale = SIMD3(repeating: 1.0)
humanEntity = character
content.add(character)
try? model.setUpIK(character)
subscription = content.subscribe(to: SceneEvents.Update.self) { event in
model.updateIK()
}
root.addChild(character)
addTargetEntities()
root.position = [0, 1.5, -1]
content.add(root)
initializePositions(for: character)
}.gesture(DragGesture().targetedToAnyEntity()
.onChanged { event in
let entity = event.entity
guard let parent = entity.parent else {return}
entity.position = event.convert(event.location3D, from: .local, to: parent)
syncPositions()
}
)
}
private func addTargetEntities() {
setInitialModelPose()
root.addChild(leftHandTarget)
root.addChild(rightHandTarget)
root.addChild(leftFootTarget)
root.addChild(rightFootTarget)
}
private func setInitialModelPose() {
leftHandTarget.position = [1.014, 0.543, -1.109]
rightHandTarget.position = [-1.014, 0.543, -1.109]
leftFootTarget.position = [0.31, -1.063, -1.07]
rightFootTarget.position = [-0.31, -1.063, -1.07]
}
private func initializePositions(for character: ModelEntity) {
Task {
// wait for model to load
var attempts = 0
let maxAttempts = 10
let retryDelayInMilliseconds = 100
while model.setUpTargets(character) == false {
attempts += 1
if attempts >= maxAttempts {
fatalError("Model failed to load in time.")
}
try? await Task.sleep(for: .milliseconds(retryDelayInMilliseconds))
}
syncPositions()
}
}
private func syncPositions() {
model.setTargetPosition(limb: .leftHand, targetPosition: leftHandTarget.position(relativeTo: humanEntity))
model.setTargetPosition(limb: .rightHand, targetPosition: rightHandTarget.position(relativeTo: humanEntity))
model.setTargetPosition(limb: .leftFoot, targetPosition: leftFootTarget.position(relativeTo: humanEntity))
model.setTargetPosition(limb: .rightFoot, targetPosition: rightFootTarget.position(relativeTo: humanEntity))
}
private static func createTargetEntity(color: UIColor) -> ModelEntity {
let radius:Float = 0.05
let entity = ModelEntity(
mesh: .generateSphere(radius: radius),
materials: [SimpleMaterial(color: color, roughness: 0.9, isMetallic: false)]
)
entity.components.set(CollisionComponent(shapes: [.generateSphere(radius: radius)]))
entity.components.set(InputTargetComponent())
entity.components.set(HoverEffectComponent())
return entity
}
}

Here's the source for SampleModel.

private class SampleModel {
var humanEntity: ModelEntity?
// Define entities to manipulate the model.
var leftHandTargetPosition = SIMD3<Float>()
var rightHandTargetPosition = SIMD3<Float>()
var leftFootTargetPosition = SIMD3<Float>()
var rightFootTargetPosition = SIMD3<Float>()
var modelInitialized = false
var targetsInitialized = false
enum Joints {
case hips
case chest
case leftHand
case rightHand
case leftFoot
case rightFoot
var name: String {
switch self {
case .hips:
"root/hips_joint"
case .chest:
"root/hips_joint/spine_1_joint/spine_2_joint/spine_3_joint/spine_4_joint/spine_5_joint/spine_6_joint/spine_7_joint"
case .leftHand:
"root/hips_joint/spine_1_joint/spine_2_joint/spine_3_joint/spine_4_joint/spine_5_joint/spine_6_joint/spine_7_joint/left_shoulder_1_joint/left_arm_joint/left_forearm_joint/left_hand_joint"
case .rightHand:
"root/hips_joint/spine_1_joint/spine_2_joint/spine_3_joint/spine_4_joint/spine_5_joint/spine_6_joint/spine_7_joint/right_shoulder_1_joint/right_arm_joint/right_forearm_joint/right_hand_joint"
case .leftFoot:
"root/hips_joint/left_upLeg_joint/left_leg_joint/left_foot_joint"
case .rightFoot:
"root/hips_joint/right_upLeg_joint/right_leg_joint/right_foot_joint"
}
}
var constraintName: String {
switch self {
case .hips:
"hips_constraint"
case .chest:
"chest_constraint"
case .leftHand:
"left_hand_constraint"
case .rightHand:
"right_hand_constraint"
case .leftFoot:
"left_foot_constraint"
case .rightFoot:
"right_foot_constraint"
}
}
}
enum Limb {
case leftHand
case rightHand
case leftFoot
case rightFoot
}
func setTargetPosition(limb: Limb, targetPosition: SIMD3<Float>) {
switch limb {
case .leftHand:
leftHandTargetPosition = targetPosition
case .rightHand:
rightHandTargetPosition = targetPosition
case .leftFoot:
leftFootTargetPosition = targetPosition
case .rightFoot:
rightFootTargetPosition = targetPosition
}
}
@MainActor
func setUpTargets(_ entity: ModelEntity) -> Bool {
// Get the limb positions in model space.
guard let leftHandModelSpace = entity.pins.set(named: "leftHand", skeletalJointName: Joints.leftHand.name).position,
let rightHandModelSpace = entity.pins.set(named: "rightHand", skeletalJointName: Joints.rightHand.name).position,
let leftFootModelSpace = entity.pins.set(named: "leftFoot", skeletalJointName: Joints.leftFoot.name).position,
let rightFootModelSpace = entity.pins.set(named: "rightFoot", skeletalJointName: Joints.rightFoot.name).position else {return false}
leftHandTargetPosition = leftHandModelSpace
rightHandTargetPosition = rightHandModelSpace
leftFootTargetPosition = leftFootModelSpace
rightFootTargetPosition = rightFootModelSpace
targetsInitialized = true
return true
}
func setUpIK(_ entity: ModelEntity) throws {
// Fetch skeleton for rig
var skeletonIterator = entity.model?.mesh.contents.skeletons.makeIterator()
guard let modelSkeleton = skeletonIterator?.next() else {
fatalError("Skeleton not found in model")
}
// Start with empty rig for the given model skeleton.
var rig = try IKRig(for: modelSkeleton)
/// Update global rig settings.
rig.maxIterations = 30
rig.globalFkWeight = 0.02
// Define constraints for the rig.
rig.constraints = [
.parent(named: Joints.hips.constraintName, on: Joints.hips.name, positionWeight: SIMD3(repeating: 90.0), orientationWeight: SIMD3(repeating: 90.0)),
.parent(named: Joints.chest.constraintName, on: Joints.chest.name, positionWeight: SIMD3(repeating: 120.0), orientationWeight: SIMD3(repeating: 120.0)),
.point(named: Joints.leftHand.constraintName, on: Joints.leftHand.name, positionWeight: SIMD3(repeating: 10.0)),
.point(named: Joints.rightHand.constraintName, on: Joints.rightHand.name, positionWeight: SIMD3(repeating: 10.0)),
.point(named: Joints.leftFoot.constraintName, on: Joints.leftFoot.name, positionWeight: SIMD3(repeating: 10.0)),
.point(named: Joints.rightFoot.constraintName, on: Joints.rightFoot.name, positionWeight: SIMD3(repeating: 10.0)),
]
let resource = try IKResource(rig: rig)
entity.components.set(IKComponent(resource: resource))
humanEntity = entity
modelInitialized = true
}
@MainActor
func updateIK() {
guard modelInitialized,
targetsInitialized,
let humanEntity,
let ikComponent = humanEntity.components[IKComponent.self] else { return }
ikComponent.solvers[0].constraints[Joints.leftHand.constraintName]?.target.translation = leftHandTargetPosition
ikComponent.solvers[0].constraints[Joints.leftHand.constraintName]?.animationOverrideWeight.position = 1.0
ikComponent.solvers[0].constraints[Joints.rightHand.constraintName]?.target.translation = rightHandTargetPosition
ikComponent.solvers[0].constraints[Joints.rightHand.constraintName]?.animationOverrideWeight.position = 1.0
ikComponent.solvers[0].constraints[Joints.leftFoot.constraintName]?.target.translation = leftFootTargetPosition
ikComponent.solvers[0].constraints[Joints.leftFoot.constraintName]?.animationOverrideWeight.position = 1.0
ikComponent.solvers[0].constraints[Joints.rightFoot.constraintName]?.target.translation = rightFootTargetPosition
ikComponent.solvers[0].constraints[Joints.rightFoot.constraintName]?.animationOverrideWeight.position = 1.0
humanEntity.components.set(ikComponent)
}
}
The skeletal model moves
 
 
Q