Add metadata-only VRM loading#46
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new initializer init(jsonDataOnly:) in BinaryGLTF to parse only the JSON chunk of a GLTF binary, and adds helper methods in VRMLoader to load metadata (VRM0.Meta) from both VRM0 and VRM1 data or URLs. It also includes corresponding unit tests. The reviewer suggested refactoring BinaryGLTF to use a private initializer to reduce code duplication between the new and existing initializers.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| public init(jsonDataOnly data: Data) throws { | ||
| var offset = MemoryLayout<UInt32>.size // skip `magic` | ||
| let rawVersion: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size) | ||
| guard let version = GLTF.Version(rawValue: rawVersion), version == .two else { | ||
| throw VRMError.notSupportedVersion(rawVersion) | ||
| } | ||
| self.version = version | ||
|
|
||
| _ = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size) as UInt32 | ||
| let chunk0Length: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size) | ||
| let chunk0Type: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size) | ||
| guard ChunkType(rawValue: chunk0Type) == .json else { | ||
| throw VRMError.notSupportedChunkType(chunk0Type) | ||
| } | ||
| let jsonData = read(data, offset: &offset, size: Int(chunk0Length)) | ||
| let decoder = JSONDecoder() | ||
| self.jsonData = try decoder.decode(GLTF.self, from: jsonData) | ||
| binaryBuffer = nil | ||
| } |
There was a problem hiding this comment.
There is significant code duplication between init(jsonDataOnly:) and the existing init(data:). We can refactor this by introducing a private initializer that handles both cases based on a boolean flag. This eliminates duplication, improves maintainability, and ensures that any future changes to the GLB parsing logic only need to be made in one place.
After applying this suggestion, you can also refactor the existing init(data:) to delegate to the private initializer:
public init(data: Data) throws {
try self.init(data: data, jsonDataOnly: false)
} public init(jsonDataOnly data: Data) throws {
try self.init(data: data, jsonDataOnly: true)
}
private init(data: Data, jsonDataOnly: Bool) throws {
var offset = MemoryLayout<UInt32>.size // skip `magic`
let rawVersion: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size)
guard let version = GLTF.Version(rawValue: rawVersion), version == .two else {
throw VRMError.notSupportedVersion(rawVersion)
}
self.version = version
let length: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size)
let chunk0Length: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size)
let chunk0Type: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size)
guard ChunkType(rawValue: chunk0Type) == .json else {
throw VRMError.notSupportedChunkType(chunk0Type)
}
let jsonData = read(data, offset: &offset, size: Int(chunk0Length))
let decoder = JSONDecoder()
self.jsonData = try decoder.decode(GLTF.self, from: jsonData)
if !jsonDataOnly, length > offset {
let chunk1Length: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size)
let chunk1Type: UInt32 = try read(data, offset: &offset, size: MemoryLayout<UInt32>.size)
guard ChunkType(rawValue: chunk1Type) == .bin else {
throw VRMError.notSupportedChunkType(chunk1Type)
}
binaryBuffer = read(data, offset: &offset, size: Int(chunk1Length)) as Data
} else {
binaryBuffer = nil
}
}
Summary
Tests