Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
vrm1 revert to shoulder
  • Loading branch information
tatsuya-ogawa committed Mar 18, 2026
commit efdd3944be151cd18fcd7c7e0bf8ac588c46b6a5
22 changes: 15 additions & 7 deletions Example/Example/RealityKitViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,27 @@ final class RealityKitViewController: UIViewController, UIGestureRecognizerDeleg
updateCameraTransform()

let neck = vrmEntity.humanoid.node(for: .neck)
let leftUpperArm = vrmEntity.humanoid.node(for: .leftUpperArm)
let rightUpperArm = vrmEntity.humanoid.node(for: .rightUpperArm)
let leftArm: Entity?
let rightArm: Entity?
switch vrmEntity.vrm {
case .v1:
leftArm = vrmEntity.humanoid.node(for: .leftShoulder)
rightArm = vrmEntity.humanoid.node(for: .rightShoulder)
case .v0:
leftArm = vrmEntity.humanoid.node(for: .leftUpperArm)
rightArm = vrmEntity.humanoid.node(for: .rightUpperArm)
}
Comment on lines +103 to +112
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for determining the arm entities based on the VRM version is duplicated across multiple example files (RealityKitViewController.swift, MacExample/ContentView.swift, VisionExample/ContentView.swift). A similar duplication exists for SCNNode in ViewController.swift and WatchExample Watch App/ViewModel.swift.

To improve code reuse and maintainability, consider abstracting this logic into computed properties on VRMEntity and VRMNode respectively.

For example, you could add this to VRMEntity:

public var leftArm: Entity? {
    switch vrm {
    case .v1: return humanoid.node(for: .leftShoulder)
    case .v0: return humanoid.node(for: .leftUpperArm)
    }
}

public var rightArm: Entity? {
    switch vrm {
    case .v1: return humanoid.node(for: .rightShoulder)
    case .v0: return humanoid.node(for: .rightUpperArm)
    }
}

This would make the call sites much cleaner.

If you prefer to keep the changes local to this file for now, you can make the existing code more concise by initializing leftArm and rightArm as a tuple.

            let (leftArm, rightArm): (Entity?, Entity?)
            switch vrmEntity.vrm {
            case .v1:
                (leftArm, rightArm) = (vrmEntity.humanoid.node(for: .leftShoulder), vrmEntity.humanoid.node(for: .rightShoulder))
            case .v0:
                (leftArm, rightArm) = (vrmEntity.humanoid.node(for: .leftUpperArm), vrmEntity.humanoid.node(for: .rightUpperArm))
            }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the logic is intended for visual adjustments specific to each example, so I think keeping it local rather than abstracting it is preferable.


let neckRotation = simd_quatf(angle: 20 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let upperArmRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let armRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
if let neck {
neck.transform.rotation = neck.transform.rotation * neckRotation
}
if let leftUpperArm {
leftUpperArm.transform.rotation = leftUpperArm.transform.rotation * upperArmRotation
if let leftArm {
leftArm.transform.rotation = leftArm.transform.rotation * armRotation
}
if let rightUpperArm {
rightUpperArm.transform.rotation = rightUpperArm.transform.rotation * upperArmRotation
if let rightArm {
rightArm.transform.rotation = rightArm.transform.rotation * armRotation
}
vrmEntity.setBlendShape(value: 1.0, for: .preset(currentExpression.preset))

Expand Down
14 changes: 12 additions & 2 deletions Example/Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,18 @@ class ViewController: UIViewController {
node.setBlendShape(value: 1.0, for: .preset(currentExpression.preset))

node.humanoid.node(for: .neck)?.eulerAngles = SCNVector3(0, 0, 20 * CGFloat.pi / 180)
node.humanoid.node(for: .leftUpperArm)?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / 180)
node.humanoid.node(for: .rightUpperArm)?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / 180)
let leftArm: SCNNode?
let rightArm: SCNNode?
switch node.vrm {
case .v1:
leftArm = node.humanoid.node(for: .leftShoulder)
rightArm = node.humanoid.node(for: .rightShoulder)
case .v0:
leftArm = node.humanoid.node(for: .leftUpperArm)
rightArm = node.humanoid.node(for: .rightUpperArm)
}
leftArm?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / 180)
rightArm?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / 180)

node.runAction(SCNAction.repeatForever(SCNAction.sequence([
SCNAction.rotateBy(x: 0, y: -0.5, z: 0, duration: 0.5),
Expand Down
22 changes: 15 additions & 7 deletions Example/MacExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,27 @@

// Adjust pose
let neck = vrmEntity.humanoid.node(for: .neck)
let leftUpperArm = vrmEntity.humanoid.node(for: .leftUpperArm)
let rightUpperArm = vrmEntity.humanoid.node(for: .rightUpperArm)
let leftArm: Entity?
let rightArm: Entity?
switch vrmEntity.vrm {
case .v1:

Check failure on line 79 in Example/MacExample/ContentView.swift

View workflow job for this annotation

GitHub Actions / Build examples (iOS/visionOS/WatchOS/macOS)

member 'v1' expects argument of type 'VRM1'
leftArm = vrmEntity.humanoid.node(for: .leftShoulder)
rightArm = vrmEntity.humanoid.node(for: .rightShoulder)
case .v0:

Check failure on line 82 in Example/MacExample/ContentView.swift

View workflow job for this annotation

GitHub Actions / Build examples (iOS/visionOS/WatchOS/macOS)

member 'v0' expects argument of type 'VRM0'
leftArm = vrmEntity.humanoid.node(for: .leftUpperArm)
rightArm = vrmEntity.humanoid.node(for: .rightUpperArm)
}

let neckRotation = simd_quatf(angle: 20 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let upperArmRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let armRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
if let neck {
neck.transform.rotation = neck.transform.rotation * neckRotation
}
if let leftUpperArm {
leftUpperArm.transform.rotation = leftUpperArm.transform.rotation * upperArmRotation
if let leftArm {
leftArm.transform.rotation = leftArm.transform.rotation * armRotation
}
if let rightUpperArm {
rightUpperArm.transform.rotation = rightUpperArm.transform.rotation * upperArmRotation
if let rightArm {
rightArm.transform.rotation = rightArm.transform.rotation * armRotation
}
vrmEntity.setBlendShape(value: 1.0, for: .custom("><"))

Expand Down
22 changes: 15 additions & 7 deletions Example/VisionExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,27 @@ final class ImmersiveViewModel {

// Adjust pose
let neck = vrmEntity.humanoid.node(for: .neck)
let leftUpperArm = vrmEntity.humanoid.node(for: .leftUpperArm)
let rightUpperArm = vrmEntity.humanoid.node(for: .rightUpperArm)
let leftArm: Entity?
let rightArm: Entity?
switch vrmEntity.vrm {
case .v1:
leftArm = vrmEntity.humanoid.node(for: .leftShoulder)
rightArm = vrmEntity.humanoid.node(for: .rightShoulder)
case .v0:
leftArm = vrmEntity.humanoid.node(for: .leftUpperArm)
rightArm = vrmEntity.humanoid.node(for: .rightUpperArm)
}

let neckRotation = simd_quatf(angle: 20 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let upperArmRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let armRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
if let neck {
neck.transform.rotation = neck.transform.rotation * neckRotation
}
if let leftUpperArm {
leftUpperArm.transform.rotation = leftUpperArm.transform.rotation * upperArmRotation
if let leftArm {
leftArm.transform.rotation = leftArm.transform.rotation * armRotation
}
if let rightUpperArm {
rightUpperArm.transform.rotation = rightUpperArm.transform.rotation * upperArmRotation
if let rightArm {
rightArm.transform.rotation = rightArm.transform.rotation * armRotation
}
vrmEntity.setBlendShape(value: 1.0, for: .custom("><"))

Expand Down
15 changes: 13 additions & 2 deletions Example/WatchExample Watch App/ViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,19 @@ final class ViewModel: ObservableObject {
let rotationOffset = model.initialRotation
node.eulerAngles = SCNVector3(0, rotationOffset, 0)

node.humanoid.node(for: .leftUpperArm)?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / 180)
node.humanoid.node(for: .rightUpperArm)?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / -180)
let leftArm: SCNNode?
let rightArm: SCNNode?
switch node.vrm {
case .v1:
leftArm = node.humanoid.node(for: .leftShoulder)
rightArm = node.humanoid.node(for: .rightShoulder)
case .v0:
leftArm = node.humanoid.node(for: .leftUpperArm)
rightArm = node.humanoid.node(for: .rightUpperArm)
}

leftArm?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / 180)
rightArm?.eulerAngles = SCNVector3(0, 0, 40 * CGFloat.pi / -180)

node.runAction(.repeatForever(.sequence([
.wait(duration: 3.0),
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,22 @@ vrmEntity.setBlendShape(value: 1.0, for: .custom("><"))
```swift
vrmEntity.setBlendShape(value: 1.0, for: .preset(.fun))
let neckRotation = simd_quatf(angle: 20 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let upperArmRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let armRotation = simd_quatf(angle: 40 * .pi / 180, axis: SIMD3<Float>(0, 0, 1))
let leftArm: Entity?
let rightArm: Entity?

switch vrmEntity.vrm {
case .v1:
leftArm = vrmEntity.humanoid.node(for: .leftShoulder)
rightArm = vrmEntity.humanoid.node(for: .rightShoulder)
case .v0:
leftArm = vrmEntity.humanoid.node(for: .leftUpperArm)
rightArm = vrmEntity.humanoid.node(for: .rightUpperArm)
}

vrmEntity.humanoid.node(for: .neck)?.transform.rotation *= neckRotation
vrmEntity.humanoid.node(for: .leftUpperArm)?.transform.rotation *= upperArmRotation
vrmEntity.humanoid.node(for: .rightUpperArm)?.transform.rotation *= upperArmRotation
leftArm?.transform.rotation *= armRotation
rightArm?.transform.rotation *= armRotation
```

### Read the thumbnail image
Expand Down
Loading