Skip to content

Merge feature/vrm1.0#42

Merged
tattn merged 6 commits intomainfrom
feature/vrm1.0
Feb 3, 2026
Merged

Merge feature/vrm1.0#42
tattn merged 6 commits intomainfrom
feature/vrm1.0

Conversation

@tattn
Copy link
Owner

@tattn tattn commented Feb 3, 2026

This pull request adds support for selecting and loading multiple VRM models in all example apps (iOS, macOS, visionOS, watchOS), introduces UI controls for switching models and blend shape expressions, and updates internal logic to handle these changes. The main improvements are the addition of a second VRM sample model, UI enhancements for model/expression selection, and refactoring of loading logic to support dynamic model switching.

Model selection and loading improvements:

  • Added a second VRM sample model (VRM1_Constraint_Twist_Sample.vrm) to the example project resources and updated Xcode project configuration to include it in all targets. (Example.xcodeproj/project.pbxproj) [1] [2] [3] [4] [5] [6]
  • Refactored model loading logic in iOS (RealityKitViewController.swift), macOS (MacExample/ContentView.swift), and visionOS (VisionExample/ContentView.swift) examples to support dynamic switching between available models via UI controls. [1] [2] [3] [4] [5] [6] [7] [8] [9]

User interface enhancements:

  • Added segmented controls to the UI for selecting both VRM models and blend shape expressions in iOS and macOS examples, improving user interactivity. [1] [2]
  • Updated visionOS and macOS example apps to use SwiftUI pickers for model selection, with real-time updates when the selection changes. [1] [2] [3]

Blend shape and pose handling:

  • Introduced enums for blend shape expressions in iOS and macOS examples, allowing users to switch facial expressions via UI controls. [1] [2] [3]
  • Updated blend shape and pose application logic to use the selected expression and model, including handling model-specific initial rotations. [1] [2] [3] [4] [5]

Build and CI improvements:

  • Added a new build-examples job to the CI workflow to build all example apps across supported platforms, ensuring new models are included in builds. (.github/workflows/ci.yml)

Project structure and type safety:

  • Added enums for model selection (VRMExampleModel, MacExampleModel, AppModel.ModelName) and updated code to use these enums for safer and clearer model handling throughout the examples. [1] [2] [3]

* vrm1.0 support

* Update Example/VisionExample/VisionExampleApp.swift

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* displayName

* displayName

* Update Example/VisionExample/ContentView.swift

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* materialName

* SpringBone

* revert Sources/VRMKit/VRM/Node.swift

* revert Sources/VRMKit/VRM/VRM1.swift

* Revert "revert Sources/VRMKit/VRM/VRM1.swift"

This reverts commit b66fe1c.

* Revert "revert Sources/VRMKit/VRM/Node.swift"

This reverts commit 445e5f6.

* fix test

* fix BlendShape

* fix comment

* remove comments

* fix comment

* mac example

* add testcase

* remove unnecessary comment

* SwiftTesting

* revert VRM1Tests.swift

* Revert "revert VRM1Tests.swift"

This reverts commit 6abaf87.

* refactor testcase

* allowedUserName

* texture transform

* fix build error

* add example build pipeline

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @tattn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request merges the 'feature/vrm1.0' branch, adding support for VRM 1.0 models to the VRMKit library. It includes migration logic to convert VRM 1.0 models to the older VRM 0.x format, updates example projects to demonstrate VRM 1.0 compatibility, and improves material and accessor handling.

Highlights

  • VRM 1.0 Support: This PR introduces support for VRM 1.0 models, including migration logic from VRM 1.0 to VRM 0.x.
  • Example Updates: The example projects (iOS, macOS, VisionOS, WatchOS) have been updated to allow switching between AliciaSolid.vrm (VRM0) and VRM1_Constraint_Twist_Sample.vrm (VRM1) models.
  • Material Handling: Improved material handling, including support for unlit materials and migration of MToon parameters from VRM 1.0 to VRM 0.x.
  • Accessor Sparse Support: Adds support for sparse accessors in VRMEntityLoader and SCNGeometrySource, improving compatibility with certain VRM models.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Example/Example.xcodeproj/project.pbxproj
    • Added VRM1_Constraint_Twist_Sample.vrm to the project.
    • Updated build settings to include the new VRM1 model.
  • Example/Example/RealityKitViewController.swift
    • Added UI to switch between VRM models (Alicia and VRM1).
    • Added UI to control expressions/blendshapes.
    • Refactored loadVRM to accept a model parameter.
    • Added RKExpression enum for RealityKit blend shapes
  • Example/Example/ViewController.swift
    • Added VRMExampleModel enum to represent available VRM models.
    • Added UI to switch between VRM models (Alicia and VRM1).
    • Added UI to control expressions/blendshapes.
    • Refactored loadVRM to accept a model parameter.
  • Example/MacExample/ContentView.swift
    • Added UI to switch between VRM models (Alicia and VRM1).
    • Refactored loadEntity to accept a model parameter.
  • Example/MacExample/ContentViewModel.swift
    • Refactored loadEntity to accept a model parameter.
    • Adjusted rotation based on selected model.
  • Example/VisionExample/ContentView.swift
    • Added UI to switch between VRM models (Alicia and VRM1).
    • Bound the picker to the app model
  • Example/VisionExample/ImmersiveView.swift
    • Refactored loadEntity to accept a model parameter.
    • Adjusted rotation based on selected model.
  • Example/VisionExample/VisionExampleApp.swift
    • Added ModelName enum to represent available VRM models.
    • Added selectedModelName to the app model
  • Example/WatchExample Watch App/ContentView.swift
    • Added UI to switch between VRM models (Alicia and VRM1).
  • Example/WatchExample Watch App/ViewModel.swift
    • Added ModelName enum to represent available VRM models.
    • Refactored loadModel to accept a model parameter.
    • Adjusted rotation based on selected model.
  • Makefile
    • Added build targets for all example projects (iOS, macOS, VisionOS, WatchOS).
  • Sources/VRMKit/VRM/Material.swift
    • Added support for KHR_materials_unlit extension.
  • Sources/VRMKit/VRM/Node.swift
    • Removed CodingKeys enum from RollAxis and AimAxis
  • Sources/VRMKit/VRM/VRM.swift
    • Added logic to detect and load VRM 1.0 models.
    • Added migration logic for VRM 1.0 data structures to VRM 0.x.
  • Sources/VRMKit/VRM/VRM1.swift
    • Updated Joint struct to allow optional physics parameters
  • Sources/VRMKit/VRM/VRMMigration.swift
    • Added VRMMigration.swift to handle migration logic from VRM 1.0 to VRM 0.x
  • Sources/VRMRealityKit/VRMEntityLoader.swift
    • Improved material handling by using UnlitMaterial for VRM shaders.
    • Added support for sparse accessors.
  • Sources/VRMSceneKit/GLTF2SCN/SCNGeometrySource+GLTF.swift
    • Added support for sparse accessors.
  • Tests/VRMKitTests/VRM1MigrationTests.swift
    • Added VRM1MigrationTests.swift to test migration logic from VRM 1.0 to VRM 0.x
  • Tests/VRMKitTests/VRM1Tests.swift
    • Updated tests to account for optional physics parameters
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/ci.yml
Activity
  • The pull request title is 'Merge feature/vrm1.0'.
  • The pull request contains changes to project settings, example code, core VRM data structures, and build configurations.
  • The pull request introduces a new file, VRMMigration.swift, to handle the conversion between VRM 1.0 and VRM 0.x formats.
  • The pull request modifies several example projects to allow users to switch between different VRM models.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces comprehensive support for VRM 1.0, a significant addition that includes new data models, a migration layer to VRM 0.x representation, and glTF sparse accessor support, along with updated examples and tests. However, a security audit identified two medium-severity Denial of Service (DoS) vulnerabilities related to insecure handling of external input within the VRM loading logic, which could cause application crashes when processing malicious or malformed models. Additionally, there is a suggestion to address code duplication for improved long-term maintainability.

Comment on lines +45 to 121
private func baseDataForSparse(accessor: GLTF.Accessor,
vectorSize: Int,
loader: VRMSceneLoader) throws -> Data {
if let bufferViewIndex = accessor.bufferView {
let bufferView = try loader.bufferView(withBufferViewIndex: bufferViewIndex)
let dataStride = bufferView.stride ?? vectorSize
return bufferView.bufferView.subdata(offset: accessor.byteOffset,
size: vectorSize,
stride: dataStride,
count: accessor.count)
}
return Data(count: vectorSize * accessor.count)
}

private func applySparse(sparse: GLTF.Accessor.Sparse,
accessorCount: Int,
vectorSize: Int,
loader: VRMSceneLoader,
data: inout Data) throws {
guard sparse.count > 0 else { return }
let indices = try sparseIndices(sparse: sparse, loader: loader)
let values = try sparseValues(sparse: sparse, vectorSize: vectorSize, loader: loader)
let count = min(indices.count, sparse.count)
data.withUnsafeMutableBytes { rawDst in
guard let dst = rawDst.bindMemory(to: UInt8.self).baseAddress else { return }
values.withUnsafeBytes { rawSrc in
guard let src = rawSrc.bindMemory(to: UInt8.self).baseAddress else { return }
for i in 0..<count {
let index = indices[i]
guard index >= 0, index < accessorCount else { continue }
let dstPos = index * vectorSize
let srcPos = i * vectorSize
memcpy(dst.advanced(by: dstPos), src.advanced(by: srcPos), vectorSize)
}
}()
}
}
}

self.init(data: bufferView,
semantic: semantic,
vectorCount: accessor.count,
usesFloatComponents: accessor.componentType == .float,
componentsPerVector: componentsPerVector,
bytesPerComponent: bytesPerComponent,
dataOffset: accessor.byteOffset,
dataStride: dataStride)
private func sparseIndices(sparse: GLTF.Accessor.Sparse, loader: VRMSceneLoader) throws -> [Int] {
let bufferView = try loader.bufferView(withBufferViewIndex: sparse.indices.bufferView)
let bytesPerIndex = bytes(of: sparse.indices.componentType)
let stride = bufferView.stride ?? bytesPerIndex
let indexData = bufferView.bufferView.subdata(offset: sparse.indices.byteOffset,
size: bytesPerIndex,
stride: stride,
count: sparse.count)
var indices: [Int] = []
indices.reserveCapacity(sparse.count)
indexData.withUnsafeBytes { raw in
guard let base = raw.baseAddress else { return }
for i in 0..<sparse.count {
let offset = i * bytesPerIndex
switch sparse.indices.componentType {
case .unsignedByte:
indices.append(Int(base.load(fromByteOffset: offset, as: UInt8.self)))
case .unsignedShort:
indices.append(Int(base.load(fromByteOffset: offset, as: UInt16.self)))
case .unsignedInt:
indices.append(Int(base.load(fromByteOffset: offset, as: UInt32.self)))
default:
break
}
}
}
return indices
}

private func sparseValues(sparse: GLTF.Accessor.Sparse,
vectorSize: Int,
loader: VRMSceneLoader) throws -> Data {
let bufferView = try loader.bufferView(withBufferViewIndex: sparse.values.bufferView)
let stride = bufferView.stride ?? vectorSize
return bufferView.bufferView.subdata(offset: sparse.values.byteOffset,
size: vectorSize,
stride: stride,
count: sparse.count)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There's significant code duplication for handling sparse accessors between this file and Sources/VRMRealityKit/VRMEntityLoader.swift. The functions baseDataForSparse, applySparse, sparseIndices, and sparseValues are nearly identical in both files. To improve maintainability, this common glTF parsing logic should be extracted to a shared location, for example, within the VRMKit module. This would avoid having to maintain two copies of the same code.

tattn and others added 3 commits February 3, 2026 20:56
Co-authored-by: tatsuya-ogawa <tatsuya-ogawa@users.noreply.github.com>
- Updated VRM.MaterialProperty to VRM0.MaterialProperty for consistency.
- Removed VRMFileProtocol as it is no longer needed.
- Migrated VRM1 structures to VRM0 equivalents in VRMMigration.swift.
- Adjusted VRMLoader to support loading VRM0 thumbnails.
- Modified Humanoid and SecondaryAnimation classes to use VRM0 types.
- Updated tests to validate VRM0 functionality and migration from VRM1.
- Removed deprecated VRM1SceneLoader and related methods.
- Added new tests for VRM0 to ensure proper functionality and migration.
@tattn tattn marked this pull request as ready for review February 3, 2026 12:52
@tattn
Copy link
Owner Author

tattn commented Feb 3, 2026

@codex please review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7611477770

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@tattn tattn merged commit 4e4f1c1 into main Feb 3, 2026
5 checks passed
@tattn tattn deleted the feature/vrm1.0 branch February 3, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants