An Objective-C glTF 2.0 loader and Metal-based renderer
This project consists of several related parts:
- GLTF.framework: A glTF 2.0 loader framework written in Objective-C (and suitable for use in Swift)
- GLTFMTL.framework: A framework for rendering glTF assets with Metal
- GLTFSCN.framework: A framework for converting glTF scenes into SceneKit scenes
- A viewer app for macOS
- A SceneKit sample app for macOS
You can add the GLTF and GLTFMTL projects as subprojects of your own Xcode project, or build them using the provided workspace and copy the resulting framework binaries into your project.
To use GLTF.framework, link against and embed GLTF.framework.
To use the provided Metal renderer, also link against and embed GLTFMTL framework.
To load a glTF 2.0 model, import <GLTF/GLTF.h>
and use the GLTFAsset
class:
GLTFAsset *asset = [[GLTFAsset alloc] initWithURL:url bufferAllocator:bufferAllocator];
The URL must be a local file URL. Loading of remote assets and resources is not supported.
bufferAllocator
is an object that knows how to allocate the memory into which glTF buffer data is loaded (it must conform to the GLTFBufferAllocator
protocol). To render an asset with the provided Metal renderer, you may pass an instance of the concrete class GLTFMTLBufferAllocator
, which is a specialized implementation of GLTFBufferAllocator
that knows how to allocate GPU-accesible memory that can be read by Metal. To create such an allocator, you will need to provide it with a reference to a previously-created MTLDevice
object:
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<GLTFBufferAllocator> bufferAllocator = [[GLTFMTLBufferAllocator alloc] initWithDevice:device];
To create Metal renderer, provide the same device that you use to create your assets:
renderer = [[GLTFMTLRenderer alloc] initWithDevice:device];
On the renderer, you need to configure the initial drawable size and pixel formats of the color and depth attachments so that they match those of the textures configured on your render passes. The Metal renderer currently does not support MSAA, nor can these pixel formats be changed once an asset has been drawn for the first time.
If you are drawing into an MTKView
, you can configure the renderer to match its settings:
renderer.drawableSize = mtkView.drawableSize;
renderer.colorPixelFormat = mtkView.colorPixelFormat;
renderer.depthStencilPixelFormat = mtkView.depthStencilPixelFormat;
The renderer is designed to allow glTF assets to be drawn into the same render command encoder (pass) as other Metal draw calls.
Here is an example of creating a command buffer and command encoder and drawing a glTF asset:
id <MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
MTLRenderPassDescriptor *renderPassDescriptor = mtkView.currentRenderPassDescriptor;
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
// ... additional command encoder configuration and draw calls ...
[renderer renderScene:asset.defaultScene
commandBuffer:commandBuffer
commandEncoder:renderEncoder];
// ... additional command encoder configuration and draw calls ...
[renderEncoder endEncoding];
[commandBuffer presentDrawable:mtkView.currentDrawable];
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
[renderer signalFrameCompletion];
}];
[commandBuffer commit];
Note that if your glTF asset contains transparent meshes, these will be drawn in the order they appear in the scene graph, and may therefore not composite correctly with opaque content or other content.
The included GLTFSCN framework can be used to easily transform glTF assets into collections of SCNScene
s to interoperate with SceneKit.
To get the collection of scenes contained in a glTF asset, use the SCNScene
class extension method +[SCNScene scenesFromGLTFAsset:options:]
. This method returns an array of scenes because there is no SceneKit type that represents a collection of scenes.
Here is an example of how to load a GLTF asset, convert it to a collection of SceneKit scenes, and access the default scene:
id<GLTFBufferAllocator> bufferAllocator = [[GLTFDefaultBufferAllocator alloc] init];
GLTFAsset *asset = [[GLTFAsset alloc] initWithURL:url bufferAllocator:bufferAllocator];
GLTFSCNAsset *scnAsset = [SCNScene assetFromGLTFAsset:asset options:@{}];
SCNScene *scene = scnAsset.defaultScene;
Note the use of the GLTFDefaultBufferAllocator
type. This is a buffer allocator that allocates regular memory rather than GPU-accessible memory. If you want to use an asset with both Metal and SceneKit, you should use the GLTFMTLBufferAllocator
(as illustrated above) instead.
Below is a checklist of glTF features and their current level of support.
- JSON
- Binary (.glb)
- External references (
buffer.uri
) - Base-64 encoded buffers
- POSITION
- NORMAL
- TANGENT
- TEXCOORD_0
- TEXCOORD_1
- COLOR_0
- JOINTS_0
- WEIGHTS_0
- Points
- Lines
- Line Loop
- Line Strip
- Triangles
- Triangle Strip
- Triangle Fan
- External image references (
image.uri
) - Base-64 encoded images
- PNG
- JPEG
- TIFF
- OpenEXR
- Radiance
- Base color factor
- Metallic factor
- Roughness factor
- Emissive factor
- Base color map
- Metallic-roughness map
- Occlusion map
- Emissive map
- Normal texture scale
- Alpha mode
- Opaque alpha mode
- Mask alpha mode
- Blend alpha mode
- Double-sided materials
- Wrap mode
- Minification/magnification filters
- Mipmaps
- Perspective cameras
- Orthographic cameras
- Morph targets
- Translation animations
- Rotation animations
- Scale animations
- Morph target weight animations
- Linear interpolation
- Discrete animations
- Cubic spline interpolation
- Joint matrix calculation
- GPU-based vertex skinning
- Sparse accessors
- KHR_materials_pbrSpecularGlossiness
- KHR_materials_common
- KHR_lights
- KHR_materials_unlit
- KHR_texture_transform
- EXT_pbr_attributes
This implementation is known to be non-conforming to the glTF 2.0 specification and is under active development.
Pull requests are gladly accepted, but will be audited strictly in order to maintain code style. If you have any concerns about contributing, please raise an issue on Github so we can talk about it.
Copyright (c) 2018 Warren Moore. All rights reserved.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.