Created
January 17, 2025 12:33
-
-
Save banjun/f0134b53937eaa7cd9339c0e0b30a994 to your computer and use it in GitHub Desktop.
create example model with skeleton for IK: https://openusd.org/dev/api/_usd_skel__schema_overview.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension ModelEntity { | |
/// create example model with skeleton: https://openusd.org/dev/api/_usd_skel__schema_overview.html | |
static func createUsdSkelExample() async throws -> ModelEntity { | |
var d = MeshDescriptor() | |
d.positions = .init([ | |
.init(0.5, -0.5, 4), .init(-0.5, -0.5, 4), .init(0.5, 0.5, 4), .init(-0.5, 0.5, 4), | |
.init(-0.5, -0.5, 0), .init(0.5, -0.5, 0), .init(-0.5, 0.5, 0), .init(0.5, 0.5, 0), | |
.init(-0.5, 0.5, 2), .init(0.5, 0.5, 2), .init(0.5, -0.5, 2), .init(-0.5, -0.5, 2)]) | |
d.primitives = .trianglesAndQuads(triangles: [], quads: [ | |
2, 3, 1, 0, | |
6, 7, 5, 4, | |
8, 9, 7, 6, | |
3, 2, 9, 8, | |
10, 11, 4, 5, | |
0, 1, 11, 10, | |
7, 9, 10, 5, | |
9, 2, 0, 10, | |
3, 8, 11, 1, | |
8, 6, 4, 11 | |
]) | |
nonisolated(unsafe) var meshResourceContents = MeshResource.Contents() | |
meshResourceContents.models = [{ | |
var model = try! MeshResource.Model(id: "model1", descriptors: [d]) | |
var part = model.parts[0] | |
part.skeletonID = "skeleton1" | |
part.jointInfluences = .init(influences: MeshBuffers.JointInfluences([2,2,2,2,0,0,0,0,1,1,1,1].map {.init(jointIndex: $0, weight: 1)}), influencesPerVertex: 1) | |
model.parts = [part] | |
return model | |
}()] | |
meshResourceContents.skeletons = [MeshResource.Skeleton(id: "skeleton1", joints: [ | |
.init(name: "Shoulder", | |
parentIndex: nil, | |
inverseBindPoseMatrix: .init([1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]).inverse, | |
restPoseTransform: .init(translation: .init(0, 0, 0))), | |
.init(name: "Shoulder/Elbow", | |
parentIndex: 0, | |
inverseBindPoseMatrix:.init([1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,2,1]).inverse, | |
restPoseTransform: .init(translation: .init(0, 0, 2))), | |
.init(name: "Shoulder/Elbow/Hand", | |
parentIndex: 1, | |
inverseBindPoseMatrix: .init([1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,4,1]).inverse, | |
restPoseTransform: .init(translation: .init(0, 0, 2))), | |
])] | |
let r = try await MeshResource(from: meshResourceContents) | |
return ModelEntity(mesh: r) | |
} | |
} | |
/// usage for example: | |
/// ```swift | |
/// let skeletonEntity = try! await ModelEntity.createUsdSkelExample() | |
/// skeletonEntity.scale = .init(repeating: 0.1) | |
/// skeletonEntity.position = .init(0, 1, -1) | |
/// content.add(skeletonEntity) | |
/// try! await setupUsdSkelExampleIK(usdSkelExampleEntity: skeletonEntity) | |
/// ``` | |
@MainActor | |
func setupUsdSkelExampleIK(usdSkelExampleEntity skeletonEntity: ModelEntity) async throws { | |
NSLog("%@", "skeletalPoses = \(String(describing: skeletonEntity.components[SkeletalPosesComponent.self]))") | |
NSLog("%@", "jointInfluences = \(String(describing: skeletonEntity.model!.mesh.contents.models[0].parts[0].buffers[.jointInfluences]))") | |
skeletonEntity.model!.materials = [{ | |
var m = SimpleMaterial(color: .green, roughness: 0.7, isMetallic: false) | |
m.triangleFillMode = .fill // .lines | |
return m | |
}()] | |
let skeleton = skeletonEntity.model!.mesh.contents.skeletons.first! | |
let jointNames = (shoulder: "Shoulder", elbow: "Shoulder/Elbow", hand: "Shoulder/Elbow/Hand") | |
var rig = try IKRig(for: skeleton) | |
rig.maxIterations = 30 | |
rig.globalFkWeight = 0.02 | |
rig.constraints = [ | |
.point(named: "hand", on: jointNames.hand, | |
positionWeight: .init(repeating: 1)), | |
.parent(named: "elbow", on: jointNames.elbow, | |
positionWeight: .init(repeating: 0.05), | |
orientationWeight: .init(repeating: 1)), | |
.parent(named: "shoulder", on: jointNames.shoulder, | |
positionWeight: .init(repeating: 0), | |
orientationWeight: .init(repeating: 0.01)) | |
] | |
rig.joints[jointNames.elbow]!.limits = .init( | |
weight: 1, boneAxis: .z, | |
minimumAngles: .init(-.pi / 2, 0, -.pi / 2), | |
maximumAngles: .init(0, 0, .pi / 4)) | |
rig.joints[jointNames.shoulder]!.limits = .init( | |
weight: 1, boneAxis: .z, | |
minimumAngles: .init(-.pi, -.pi / 2, 0), | |
maximumAngles: .init(.pi, .pi / 4, 0)) | |
let resource = try IKResource(rig: rig) | |
skeletonEntity.components.set(IKComponent(resource: resource)) | |
} | |
@MainActor | |
struct HandIKSystem: System { | |
static let query = EntityQuery(where: .has(IKComponent.self)) | |
init(scene: RealityKit.Scene) {} | |
func update(context: SceneUpdateContext) { | |
context.entities(matching: Self.query, updatingSystemWhen: .rendering).forEach { e in | |
let ik = e.components[IKComponent.self]! | |
ik.solvers[0].constraints["hand"]!.target.translation = .init( | |
3 * Float(cos(2 * NSDate().timeIntervalSince1970)), | |
3 * Float(sin(2 * NSDate().timeIntervalSince1970)), | |
0.5) | |
// NSLog("%@", "ik...target = \(ik.solvers[0].constraints["hand"]!.target.translation)") | |
ik.solvers[0].constraints["hand"]!.animationOverrideWeight.position = 1 | |
e.components.set(ik) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment